This is an R Markdown Notebook. When you execute code within the notebook, the results appear beneath the code.
These notebooks are typically this is designed to create a pleasing viewing environment of data analysis that allows you to include figures, text, links, etc. so that your work is better understood and can be reproduced and used with confidence.
The source code for this R notebook (Rmd suffixed files), when stored as web pages (html files), can be downloaded by clicking the button at the top of the page.
If viewing the source code in R Studio, try executing each R “chunk” by clicking the Run button within the chunk or by placing your cursor inside it and pressing Cmd+Shift+Enter. z
Warning. Typos are Legion!
1. Introduction
When you’re in MATH 381 (Intro to Probability and Stats) you’ll get a taste of R. R is an open-source statistical package build off of an earlier generation of commercial.
The goal here is to demonstrate cracking open an excel spreadsheet in R and calculate some basic stats, create various plots to view the statistics, and finally, do some linear and multivariate regression
Another goal here is to show off some of R’s features. R is a very powerful tool. When translating “powerful” from computereese to any frustrated human dialect, that means “steep learning curve.” It’s also a community-supported environment. When translating “powerful” from computereese to any overscheduled human dialect, that means “there are LOTS of people donating packages and libraries to R.” Some have evolved to be a standard in the community. Others are highly specialized for a given discipline (but have one or two items that people outside their user communities find handy.)
But don’t let that intimidate you. Once you learn one language you can slowly pick up more. Also with this demo we aren’t going to get to to be an R guru in a day.
If you want a good stepping off point to learn R I’d recommend some of the resources at Data Camp which have some free starter tutorials for R.
2. Loading the Libraries
To work with R we will first have to load some libraries. This is like in C where you have the #include statement to do things like raise things to powers and stuff like that.
Some of these libraries or “packages” come with R. Others will have to be installed. Here are the ones we are using for this exercise.
Also in this exercise, we’re going to use the tidyverse set of packages. Tidyverse is a set of co-developed tools for data science in R. This is the new big thing in R and is widely used so we are just going to jump in here. SD Mines has a course beyond Engineering Stats, MATH 443/543 (Data Analysis) that leverages this set of packages.
- Install Us First
- tidyverse : Set of commonly-used Data Science packages for R that it can install and load all at once. In the long-run you probably also want to install the tidyverse package suite anyway. For this exercise this will include…
- ggplot2 : Create Elegant Data Visualizations Using the Grammar of Graphics
- tibble : Simple Data Frames
- tidyr : Tools for shepherding data in data frames.
- readr : Read Rectangular Text Data
- purr : Functional Programming Tools
- dplyr : A grammar of data manipulation
- stringr : Simple, Consistent Wrappers for Common String Operations
- forcats : Tools for Working with Categorical Variables (Factors)
- readxl : also part of the tidyverse package suite for reading traditional excel spreadsheets.
- moderndive : Tidyverse-Friendly Introductory Linear Regression
- This should come with R’s core install, if not install ’em.
- MASS : Has a lot of resources for regression.
- This doesn’t come with R’s core install so install that one…
- moments : This has a load of good stuff for data analysis and plotting, more than you will need here, but get it anyway.
- This is a nice contributed library that lets us make pretty statistics tables. It was written for ecological applications but it’s still pretty handy for looking at concrete
- pastecs: Package for Analysis of Space-Time Ecological Series
- Another nice contributed library that makes matrices of correlation coefficients look pretty (and graphically informative).
- corrplot Visualization of a Correlation Matrix
- While not officially needed for this activity but I’ll demonstrate how units can be used in R in this example
- udunits2 Provides simple bindings to Unidata’s udunits library for unit conversions (will be demonstrating but not explicity needing it here)
- units Provides Measurement Units for R Vectors
# Tidyverse Handling Libraries
library(package = "tidyverse") # main tidyverse suite
library(package = "readxl") # Read Excel Files
library(package = "moderndive") # regression support
# Statistics Libraries
library(package = "moments") # Moments, cumulants, skewness, kurtosis and related tests
library(package = "MASS") # Support Functions and Datasets for Venables & Ripley's MASS text
# Extra Graphics Libraries
library(package = "corrplot") # Visualization of a Correlation Matrix
# Data Processing Libraries
library(package = "pastecs") # Package for Analysis of Space-Time Ecological Series
library(package = "udunits2") # Unit Conversion Support
# library(package = "units") # Measurement Units for R Vectors
3. Cracking a Spreadsheet
The spreadsheet example below is a more complicated than what you hopefully have.
The original data set is from a set of papers on Concrete by I-Cheng Yeh
Yeh, I-Cheng, “Modeling slump of concrete with fly ash and superplasticizer,” Computers and Concrete, 5(6), 559-572, 2008. doi: 10.12989/cac.2008.5.6.559.
Yeh, I-Cheng, “Simulation of concrete slump using neural networks,” Construction Materials, 162(1), 11-18, 2009. doi: 10.1680/coma.2009.162.1.11
Yeh, I-Cheng, “Prediction of workability of concrete using design of experiments for mixtures,” Computers and Concrete, 5(1), 1-20, 2008. doi: 10.12989/cac.2008.5.1.001
Yeh, I-Cheng, “Modeling slump flow of concrete using second-order regressions and artificial neural networks,” Cement and Concrete Composites, 29(6), 474-480, 2007. doi: 10.1016/j.cemconcomp.2007.02.001
Yeh, I-Cheng, “Exploring concrete slump model using artificial neural networks,” ASCE J. of Computing in Civil Engineering, 20(3), 217-221, 2006. doi: 10.1061/(ASCE)0887-3801(2006)20:3(217)
and is kept at the UC-Irvine Machine Learning Repository.
It can be found here at http://kyrill.ias.sdsmt.edu/cee_284/Base_Concrete_Slump_Test_for_R.xlsx
The relevant page and screenshot is below. For drama-free R import you are probably best off keeping a page on your spreadsheet file that is very simple, with numbers going down, and a single line for Row-1 with the headers of each column. If you want to get fancy on other pages that you’d turn in as tables in reports, you can do that on another spreadsheet page.
To crack open the spreadsheet we will want to use the read_excel function.
You can read the spreadsheet from a local drive or from a website.
# you will need the full path to the file you are using (either online or locally on your disk)
# The if else block should query your machine to determine which operating system.
# if you are not bi-platform, you likely don't need this.
if(.Platform$OS.type == "windows") {
# Windows
spreadsheet_name = "%HOMEPATH%/Downloads/Base_Concrete_Slump_Test_for_R.xlsx"
} else {
# Unix (Linux, MacOS, Solaris)
spreadsheet_name = "~/Downloads/Base_Concrete_Slump_Test_for_R.xlsx"
}
# I am keeping a copy of these spreadsheet at the URL below. It can be downloaded automatically
# and then loaded. We can also discretely delete it when done.
spreadsheet_url = "http://kyrill.ias.sdsmt.edu/wjc/eduresources/Base_Concrete_Slump_Test_for_R.xlsx"
download.file(url = spreadsheet_url, # URL location
destfile = spreadsheet_name) # local downloaded location
trying URL 'http://kyrill.ias.sdsmt.edu/wjc/eduresources/Base_Concrete_Slump_Test_for_R.xlsx'
Content type 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' length 18736 bytes (18 KB)
==================================================
downloaded 18 KB
remove(spreadsheet_url) # clean up variables
# this command will read the file
concrete = read_excel(path = spreadsheet_name, # remove spreadsheet location
sheet = "Data", # page of spreadsheet
col_names = TRUE) # first row are the column headers
# clean up your hard drive! Don't be like me!
if(.Platform$OS.type == "windows") {
# Windows
system(str_c("DEL ",
spreadsheet_name,
sep=""))
} else {
# Unix (Linux, MacOS, Solaris)
system(str_c("rm -v ",
spreadsheet_name,
sep=""))
}
/Users/wjc/Downloads/Base_Concrete_Slump_Test_for_R.xlsx
remove(spreadsheet_name) # clean up variables
With the data read in we can now look at the table of the data. This looks much nicer when working in R Notebooks instead of Plain Ordinary R.
# Print data frame
colnames(concrete)[1] = "Test_Number"
print(concrete)
NA
4. Some Basic Statistics and Traditional Single Variable Plots
Lets start with some basic statistics and plotting of them.
4.1. The “classic” stats
Let’s get the mom-and-apple-pie stats for Concrete That second argument allows you to deal with missing data.
# statistics for cement
print(str_c(" Mean Cement : ",
mean(x = concrete$Cement, # variable to crunch
na.rm = TRUE) # ignore msissing data
))
[1] " Mean Cement : 229.894174757282"
print(str_c(" Stdev Cement : ",
sd(x = concrete$Cement, # variable to crunch
na.rm = TRUE) # ignore msissing data
))
[1] " Stdev Cement : 78.8772300268858"
print(str_c("Skewness Cement : ",
skewness(x = concrete$Cement, # variable to crunch
na.rm = TRUE) # ignore msissing data
))
[1] "Skewness Cement : 0.143018080025135"
print(str_c("Kurtosis Cement : ",
kurtosis(x = concrete$Cement, # variable to crunch
na.rm = TRUE) # ignore msissing data
))
[1] "Kurtosis Cement : 1.33448397363582"
OK this is a little clunky. It would be nice if someone somewhere made a support library for R that will make nice tables of statistics.
In this case Vive La France! A team from French Research Institute for Exploitation of the Sea thought the same question and as is often the case for the R community not only drafted a set of tools to do this, and made it public.
Here we ware using their stat.desc function.
This will hopefully give people wanting to make basic tables “maximum satisfaction with minimal effort.”
# Plot a statistics table -- all the classics nice and handy and pretty.
options(digits=2) # this simply set the decimal count in the table to be created below
# this particular function creates the table in scientific notation
concrete_statistics = stat.desc(x = concrete, # data frame
basic = TRUE, # includes counts and extremes
desc = TRUE, # include classic stats (mean etc)
norm = TRUE, # include normal dist stats (skewness etc)
p = 0.95) # use 95% confidence limits
print(concrete_statistics)
NA
4.2. Reorganizing Your Data to Handle Multiple Variables at Once
To leverage some of R’s more nifty features we will need to reorganize our data from a “spreadsheet style” format to what some people have called a “long form” table so that the column headers of our concrete traits become a single column with the values in the columns placed all into a single column similar to the graphic below.
This is done with the function gather()
# Gathering our components into a single column.
# We just want the names of our components here so we get everything past
# the first column (which is the experiment name)
column_names = colnames(concrete[2:ncol(concrete)])
tbl_df(column_names) # tbl_df makes it look pretty when printed
# the gather command will group everything. in the column name group
concrete_tidy = gather(data = concrete, # your data frame
key = "Parameter", # column name for your former columns
value = "Value", # column name for your data
column_names ) # the list for the columns to "gather"
# this will let us sort future plots in the same order as our plots.
concrete_tidy$Parameter = factor(x = concrete_tidy$Parameter,
levels = column_names)
# we can also split things between our dependant variables and independant variables.
concrete_independent = subset(x = concrete_tidy,
subset = (Parameter != "Slump") &
(Parameter != "Flow") &
(Parameter != "Compressive_Strength_28dy")
)
concrete_dependent = subset(x = concrete_tidy,
subset = (Parameter == "Slump") |
(Parameter == "Flow") |
(Parameter == "Compressive_Strength_28dy")
)
print(concrete_tidy)
print(concrete_independent)
print(concrete_dependent)
NA
NA
5. Plotting Graphics using Tidyverse Resources
R has a few ways to do the basic histograms, Boxplots and other distribution plots.
There are a number of spiffy ways to plot these statistical plots in R. We’re just using one here…
5.1. SLOOOOWWWWLLLLLYYY Making a Simple Plot (Histogram Edition)
Now I’m going to do this one tiny step at a time until we get to a viable product. (This is how I work through cryptic procedures so I can see what each little additional mystery thingie does.)
Graphing is invoked by the ggplot2 command.. which has a heluvalot under its hood! For me all that detail was what had me a little shy to adopt this way of printing data.
Tidyverse uses what is sometimes called the “grammar of graphics” method… to make a long story longer, the GoG presents separate commands to do separate things rather bundle stuff in a single graphing function. Sometimes it makes a lot of sense… other times it may be confusion. (Hence me demonstrating making a graph this one tiny step at a time!
First thing we are going to do is open a plotting space with the command ggplot()
# invoke the ggplot plotting environmnent.
ggplot()

Wow. We have a… big square of… grey. All it’s doing is setting up our plot environment… so let’s do some more…
If we want to do a histogram we are going to have to tell it what we want to print and where to get the stuff
When we add things to a plot command in Tidyverse we “add” to the steps incrementally.
This involves a “mapping” function called “aes” (short for aesthetics)
here, we are working with the data frame “concrete” and are working on the variable Cement which we are tossing onto the x axis because that’s where the bins of cement go!
ggplot(data = concrete) + # EDIT: invoke graphics environment using a given dataframe
aes(x = Cement) # NEW: select variable to print... You can get really fancy here later

OK now we have something that looks like we may have the making of the graph. If you don’t like grey outlines and white grids, no worries, we can change that shortly.
OK.. we are now ready to make a histogram…
Here we will use one of the gglot2’s "geom_*" (draw stuff) resources. The default should work for us here.
ggplot(data = concrete) + # invoke graphics environment using a given dataframe
aes(x = Cement) + # select variable to print... You can get really fancy here later
geom_histogram() # NEW: insert histogram

(you may have gotten a warning about using the bin=X, you can adjust it.)
Now quickly before moving on… I am not keen on the grey background with white lines.
There are a number of out-of-the-box “themes” for ggplot2.
I’m partial to theme_bw() and theme_light() but try the ones that you prefer or stick with the default, theme_gray().
These plots shown here are mine. You should fidget about so they are yours and so you can adapt to this new way of working with data.
ggplot(data = concrete) + # invoke graphics environment using a given dataframe
theme_bw() + # NEW: changing the plotting theme
aes(x = Cement) + # select variable to print... You can get really fancy here later
geom_histogram() # insert histogram (including controlling number of bins)

My OCD hates axes where the labels don’t envelop all of the data…
We can fix that with xlim() or ylim()
ggplot(data = concrete) + # invoke graphics environment using a given dataframe
theme_bw() + # changing the plotting theme
aes(x = Cement) + # select variable to print... You can get really fancy here later
xlim( 100, 400 ) + # NEW: adding x-axis limits
geom_histogram() # insert histogram

How about changing the color of the fill in the bars…
You really don’t want to know about all the colors you can use.
ggplot(data = concrete) + # invoke graphics environment using a given dataframe
theme_bw() + # changing the plotting theme
aes(x = Cement) + # select variable to print... You can get really fancy here later
xlim( 100, 400 ) + # NEW: adding x-axis limits
geom_histogram(fill="gray") # EDIT: insert histogram (with a single chosen color)

Want to customize the labels and titles so we can have units?
You can add custom labels and titles! (https://www.nceas.ucsb.edu/~frazier/RSpatialGuides/colorPaletteCheatsheet.pdf)
For the superscripting in the x-axis label, I am using the expression() tool in R.
ggplot(data = concrete) + # invoke graphics environment using a given dataframe
theme_bw() + # changing the plotting theme
aes(x = Cement) + # select variable to print... You can get really fancy here later
xlim( 100, 400 ) + # adding x-axis limits
ggtitle("Yeh Superplasticizer Tests") + # NEW : Custom Title
xlab(expression('Cement Amount (kg m'^-3*")")) + # NEW : Custom Axis Label
geom_histogram(fill="gray") # insert histogram (with a single chosen color)

And I could keep tweaking this graph all day, but good enough is good enough so this is a good place to stop…
We also can plot a few other fields with some trial and error..
# Histogram of Water
ggplot(data = concrete) + # invoke graphics environment using a given dataframe
theme_bw() + # changing the plotting theme
aes(x = Water) + # select variable to print... You can get really fancy here later
xlim( 150, 250 ) + # adding x-axis limits
ggtitle("Yeh Superplasticizer Tests") + #Custom Title
xlab(expression('Water Amount (kg m'^-3*")")) + # NEW : Custom Axis Label note use of superscripts from above
geom_histogram(fill="blue") # insert histogram (with a single chosen color)

# Histogram of Strength
ggplot(data = concrete) + # invoke graphics environment using a given dataframe
theme_bw() + # changing the plotting theme
aes(x = Compressive_Strength_28dy) + # select variable to print... You can get really fancy here later
xlim( 10, 60 ) + # adding x-axis limits
ggtitle("Yeh Superplasticizer Tests") + #Custom Title
xlab("28-dy Compressive Strength (MPa)") + # NEW : Custom Axis Label
geom_histogram(fill="red") # insert histogram (with a single chosen color)

(And from our Intro to Stats Lecture…)
# Histogram of Strength
ggplot(data = concrete) + # invoke graphics environment using a given dataframe
theme_bw() + # changing the plotting theme
aes(x = Slump) + # select variable to print... You can get really fancy here later
xlim( 0, 30 ) + # adding x-axis limits
ggtitle("Yeh Superplasticizer Tests") + #Custom Title
xlab("Slump (cm)") + # NEW : Custom Axis Label
geom_histogram(fill="darkgreen") # insert histogram (with a single chosen color)

5.2 Distribution Plot [not so good an] Example
There are some other plots that we can use to describe our data.
Here to play with them we will take a quick step back and address that “tidy”’ed (should that say “tidied”?) dataframe “concrete_tidy”
We can now use all the parameters in the “tidy” (long) data frame to print by specific traits.
ggplot(data = concrete_tidy) + # invoke graphics environment using a given dataframe
theme_bw() + # changing the plotting theme
aes(x = Value, # map x-axis value
color = Parameter) + # map colors for different quality
ggtitle("Yeh Superplasticizer Tests") + # Custom Title
xlab("Value") + # Custom Axis Label
geom_density() # insert crete a relative density plot

In the past, I’ve gotten good results with this but in this case, I think it’s too messy in part due to the disparity in the dynamic range of our parameters.
5.3. Box-Whisker Plot Example
How about leveraging a box whisker? (I’m using only the independent variables this time.)
ggplot(data = concrete_independent) + # EDIT Changing dataframe
theme_bw( ) + # changing the plotting theme
theme(axis.text.x = element_blank()) + # adding an extra trait to the x-axis
# to not print labels on the x-axis
# (the labels overlap and doesn't look
# pretty...)
aes(y = Value, # map y-axis value
x = Parameter, # map x-axis value
color = Parameter) + # map colors for different quality
ggtitle(label = "Yeh Superplasticizer Tests",
subtitle = "Concrete Test Components") + # Custom Title
ylab(expression('Amount (kg m'^-3*")")) + # EDIT : Changing Custom Axis Label
geom_boxplot() # insert crete a relative density plot

What about our dependant variables? We can start by changing the data frame…
ggplot(data = concrete_dependent) + # EDIT Changing dataframe
theme_bw( ) + # changing the plotting theme
theme(axis.text.x = element_blank()) + # adding an extra trait to the x-axis
# to not print labels on the x-axis
# (the labels overlap and doesn't look
# pretty...)
aes(y = Value, # map y-axis value
x = Parameter, # map x-axis value
color = Parameter) + # map colors for different quality
ggtitle(label = "Yeh Superplasticizer Tests",
subtitle = "Concrete Test Results") + # Custom Title
ylab("Values") +
geom_boxplot() # insert crete a relative density plot

Want units? That’s a little tougher here since the units differ by parameter. We can force the values to into new names though.
ggplot(data = concrete_dependent) + # EDIT Changing dataframe
theme_bw( ) + # changing the plotting theme
theme(axis.text.x = element_blank()) + # adding an extra trait to the x-axis
# to not print labels on the x-axis
# (the labels overlap and doesn't look
# pretty...)
aes(y = Value, # map y-axis value
x = Parameter, # map x-axis value
color = Parameter) + # map colors for different quality
ggtitle(label = "Yeh Superplasticizer Tests",
subtitle = "Concrete Test Results") + # Custom Title
ylab("Values") +
# NEW: It says scale color but "color" is how we are distinguishing
# out boxplots (as seen in the mapping/aes command)
# we can then use the same plot order above to rewrite the labels
# (likewise we could change the plot order and of coruse the colors.)
scale_color_discrete(labels = c("Slump (cm)",
"Flow (cm)",
"28dy-Compresional Stress (mPa)")) +
geom_boxplot() # insert crete a relative density plot

NA
NA
5.4. Violin Plot Example
How about leveraging a “violin” plot? A violin plot’s width swells in areas with more observations and contracts with sparser data so it is like looking at a probability distribution.
ggplot(data = concrete_independent) + # EDIT Changing dataframe
theme_bw( ) + # changing the plotting theme
theme(axis.text.x = element_blank()) + # adding an extra trait to the x-axis
# to not print labels on the x-axis
# (the labels overlap and doesn't look
# pretty...)
aes(y = Value, # map y-axis value
x = Parameter, # map x-axis value
color = Parameter) + # map colors for different quality
ggtitle(label = "Yeh Superplasticizer Tests",
subtitle = "Concrete Test Components") + # Custom Title
ylab(expression('Amount (kg m'^-3*")")) + # Changing Custom Axis Label
geom_violin(scale="width") # EDIT: change to a violin plot

# the width argument
# gives every plot the same width
and…
ggplot(data = concrete_dependent) + # EDIT Changing dataframe
theme_bw( ) + # changing the plotting theme
theme(axis.text.x = element_blank()) + # adding an extra trait to the x-axis
# to not print labels on the x-axis
# (the labels overlap and doesn't look
# pretty...)
aes(y = Value, # map y-axis value
x = Parameter, # map x-axis value
color = Parameter) + # map colors for different quality
ggtitle(label = "Yeh Superplasticizer Tests",
subtitle = "Concrete Test Results") + # Custom Title
ylab("Values") +
# NEW: It says scale color but "color" is how we are distinguishing
# out boxplots (as seen in the mapping/aes command)
# we can then use the same plot order above to rewrite the labels
# (likewise we could change the plot order and of coruse the colors.)
scale_color_discrete(labels = c("Slump (cm)",
"Flow (cm)",
"28dy-Compresional Stress (mPa)")) +
geom_violin(scale="width") # EDIT: change to a violin plot

# the width argument
# gives every plot the same width
This is basically the above “density” plot but “looking down” as with a box plot. Also here we are trimming the plot so that when we leave the range of any of the data points, the “violins” are truncated.
5.5. Stacked Column or Bar Plot Example
We also can do bar plots or stacked column plots. The one produced here shows the combined components by test unit.
ggplot(data = concrete_independent) + # EDIT Changing dataframe
theme_bw( ) + # changing the plotting theme
aes(x = Test_Number,
y = Value,
fill = Parameter) + # map colors for different quality
ggtitle(label = "Yeh Superplasticizer Tests",
subtitle = "Concrete Test Components") + # Custom Title
ylab(expression('Amount (kg m'^-3*")")) + # Changing Custom Axis Label
geom_col(position = "stack", # new, create a stacekd column graph
width = 1.0 ) # with no space between columns

6. Correlation of Variables
6.1. Correlating and then Fitting Cement to Compressive Strength
Let’s start by doing a “simple”" plot . In this case since I already know the answer because the spreadsheet also has a table of how well our independent variables correlate against the dependent variables (e.g., Slump, Flow, or in our case Strength). The Cement correlates the best against Compressive Strength (OK, truth be told, it correlates the least badly).
We can actually do this with a correlate function, cor()…
To grab a value in the table “concrete” we call the data frame (concrete) and the variable name (Cement or Water vs Compressive_Strength_28dy), separating the frame and variable names by a $ sign.
print("Cement vs Compressive Strength Correlation, r")
[1] "Cement vs Compressive Strength Correlation, r"
cor(x = concrete$Cement, # the x-value
y = concrete$Compressive_Strength_28dy, # the y-value
method = "pearson" # method of correlation
)
[1] 0.45
or if you like to do everything at once…
# calculate all correlation values against each other
correlation_matrix = cor(x = concrete, # using our dataframe to correlate evything
method = "pearson" )
tbl_df(correlation_matrix)
NA
Lots of numbers… not all that insightful on their own…
You also can graph the look-n-feel of what all of the different correlations are… (it works best with a much smaller number of variables)
# draw a coorelation graphic...
corrplot(corr = correlation_matrix,
type = "upper")

We can now see for example that cement, slag, and fly ash amounts have a nominal but not thrilling correlation to compression strength while water has a good correlation with the resulting slump values. One thing that this does not show is how well these parameters play with other parameters. As we’ll see when all of our independent values are working together we’ll discover that cement and water, followed by fly ash and coarse aggregates will, together, contribute the most of our independent parameters in calculating the compressive strength.
6.2. Scatter Plot Example
But for now, let’s plot plot the Cement amount against Compressive Strength
# Making a simple X-Y scatterplot.
ggplot(data = concrete) + # invoke graphics environment using a given dataframe
theme_bw( ) + # changing the plotting theme
aes(x = Cement, # x-value
y = Compressive_Strength_28dy) + # y-value
ggtitle("Yeh Superplasticizer Tests") + # Custom Title
xlab(expression('Cement Amount (kg m'^3*")")) + # x-label
ylab("28-dy Compressive Strength (MPa)") + # y-label
geom_point(colour="grey") # EDIT: plot points the color keyword part was

# writen by an anglophile!
Here’s a cute trick: Could we color those dots by a variable?
Sure!
# Making a simple X-Y scatterplot now coloured by another parameter
ggplot(data = concrete) + # invoke graphics environment using a given dataframe
theme_bw( ) + # changing the plotting theme
aes(x = Cement, # x-value
y = Compressive_Strength_28dy, # y-value
color = Superplasticizer) + # ADD: we can color by a variable!
ggtitle("Yeh Superplasticizer Tests") + # Custom Title
xlab(expression('Cement Amount (kg m'^3*")")) + # x-label
ylab("28-dy Compressive Strength (MPa)") + # y-label
geom_point() + # plot points
scale_color_distiller(palette = "Spectral") # NEW: pick a custom "colour" palate.

Love overkill without any distinct numerical score and look at how everything in your data set correlates with every other variables…?
Try pairs()
(I like the corrplot function better!)
# way too many tiny plots!
pairs(x = concrete, # do everything in the dataframe
pch = ".") # plot dots (the default is circles)

(Obviously the more variables in your dataframe the messier it gets!)
6.3. Creating our linear model and “calibrating” it
We weren’t all that thrilled with the correlation between these components and strength but let’s go ahead and demonstrate a regression.
But let’s move on and create a regression model from this.
Here we will use the lm() (linear model) function from the MASS package.
For the regression formula
\(\widehat{y}(x) = {\alpha_0}+{\alpha_1}\ x\)
or
\(\widehat{Strength}(concrete) = {\alpha_0}+{\alpha_1}\ concrete\)
the “prototype” (formula) for the function is written as …
“Y ~ X” (with the y-intercept implicit in the formula… you don’t put it in but it’ll be there when you’re done.)
The above syntax is works like this….
Dependent Variable [~ is a function of ] Independent Variable [and any other parameter you need gets added with a plus]
If this were a \(\widehat{y}(x)={\alpha_0}+{\alpha_0}\ x^3\), then the prototype for the function would be y ~ x^3
This will hopefully make more sense as we continue!
(lm and similar linear regression functions don’t play well with units.)
linear_model.S_v_c = lm(formula = Compressive_Strength_28dy ~ Cement, # your formula y ~ x
data = concrete) # the data frame
Let’s see what we have… This summary command will provide the details of the lm() function’s important results
For us we want to see the Y-Intercept [the (Intercept) under “Estimate”] and the slope that goes with our independent value (“Concrete” under “Estimate”)
The Standard Error of the Estimate is there (Residual Standard Error) as is the Coefficient of Determination (Multiple R-squared)
We’ll talk about a few of the other features when we do the larger multivariate regression
summary(object = linear_model.S_v_c)
Call:
lm(formula = Compressive_Strength_28dy ~ Cement, data = concrete)
Residuals:
Min 1Q Median 3Q Max
-15.134 -5.313 0.832 5.155 17.968
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 25.85676 2.15022 12 < 2e-16 ***
Cement 0.04429 0.00885 5 2.4e-06 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 7 on 101 degrees of freedom
Multiple R-squared: 0.199, Adjusted R-squared: 0.191
F-statistic: 25 on 1 and 101 DF, p-value: 2.38e-06
In the above output, the asterisk identify the most significant independent variables. Here it’s trivial even though this is a terrible relationship between cement and strength. Later we will use all of our available independent variables and the use of these asterisks will become more important.
Want to plot it?
Good news?
Like Excel, you have some automated features to give you quick satisfaction and happiness. More still, it will give you confidence limits.
For this we use an extension to the graphics package called geom_smooth()
# Making a simple X-Y scatterplot and adding a regression to it
ggplot(data = concrete) + # invoke graphics environment using a given dataframe
theme_bw( ) + # changing the plotting theme
aes(x = Cement, # x-value
y = Compressive_Strength_28dy) + # y-value
ggtitle("Yeh Superplasticizer Tests") + # Custom Title
xlab(expression('Cement Amount (kg m'^-3*")")) + # x-label
ylab("28-dy Compressive Strength (MPa)") + # y-label
geom_point(colour="darkgrey") + # plot points
geom_smooth(method = "lm", # use a simple linar model
formula = y ~ x, # lm-style formula
se = TRUE, # splay Confidence Intervals
level = 0.95, # Confidene Level to Map Out
colour = "black", # regression line color
size = 0.5) # line thickness

The line here looks like a positive correlation between the cement amount and the resulting strength.
Let’s try water:
# getting the linear model
linear_model.S_v_w = lm(formula = Compressive_Strength_28dy ~ Water, # your formula y ~ x
data = concrete ) # the data frame
summary(linear_model.S_v_w)
Call:
lm(formula = Compressive_Strength_28dy ~ Water, data = concrete)
Residuals:
Min 1Q Median 3Q Max
-19.359 -5.451 -0.986 4.690 18.825
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 55.4824 7.3978 7.50 2.5e-11 ***
Water -0.0986 0.0373 -2.64 0.0096 **
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 7.6 on 101 degrees of freedom
Multiple R-squared: 0.0646, Adjusted R-squared: 0.0554
F-statistic: 6.98 on 1 and 101 DF, p-value: 0.00956
# Making a simple X-Y scatterplot and adding a regression to it
ggplot(data = concrete) + # invoke graphics environment using a given dataframe
theme_bw( ) + # changing the plotting theme
aes(x = Water, # x-value
y = Compressive_Strength_28dy) + # y-value
ggtitle("Yeh Superplasticizer Tests") + # Custom Title
xlab(expression('Water Amount (kg m'^-3*")")) + # x-label
ylab("28-dy Compressive Strength (MPa)") + # y-label
geom_point(colour="darkblue") + # plot points
geom_smooth(method = "lm", # use a simple linar model
formula = y ~ x, # lm-style formula
se = TRUE, # splay Confidence Intervals
level = 0.95, # Confidene Level to Map Out
colour = "blue", # regression line color
fill = "cyan", # NEW: fill for confidence limits
size = 0.5) # line thickness

Looking up back the tables none of the variables
7. Multivariate Linear Regression
And now we’re going to do something about that!
We’re now going to use not just one independent variable… but all 7 of them!
The good news is that it follows the same form as the simple linear regression. This time we string along all of our independent variables with in our formula prototype.
Our formula now has multiple independent values but still follows the same style of solution…
\(\widehat{y}(\mathbf{x}) = {\alpha_0}+{\alpha_1} x_1 + {\alpha_2} x_2 + {\alpha_2} x_3 + ... +{\alpha_n} x_n\)
linear_model.S_v_all <- lm(data = concrete, # your data frame
formula = Compressive_Strength_28dy ~ Cement + # your formula
Slag +
Fly_Ash +
Water +
Superplasticizer +
Fine_Aggregates +
Coarse_Aggregates)
And here are these results…
summary(object = linear_model.S_v_all)
Call:
lm(formula = Compressive_Strength_28dy ~ Cement + Slag + Fly_Ash +
Water + Superplasticizer + Fine_Aggregates + Coarse_Aggregates,
data = concrete)
Residuals:
Min 1Q Median 3Q Max
-5.841 -1.706 -0.283 1.299 7.942
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 139.7815 71.1013 1.97 0.0522 .
Cement 0.0614 0.0228 2.69 0.0084 **
Slag -0.0297 0.0318 -0.94 0.3520
Fly_Ash 0.0505 0.0232 2.18 0.0316 *
Water -0.2327 0.0717 -3.25 0.0016 **
Superplasticizer 0.1031 0.1346 0.77 0.4453
Fine_Aggregates -0.0391 0.0288 -1.36 0.1783
Coarse_Aggregates -0.0556 0.0274 -2.03 0.0455 *
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 2.6 on 95 degrees of freedom
Multiple R-squared: 0.897, Adjusted R-squared: 0.889
F-statistic: 118 on 7 and 95 DF, p-value: <2e-16
Our regression coefficients are still here under the “Estimate” column as are our Standard Error of our Estimate and our Coeff of Determination.
Also we can now take a good look at those asterisks at the end of line with the parameter coefficients. These can explain which independent variables do the heaviest lifting in our regression. The more asterisks, the more important the dependent variable is to the larger multivariate regression. Here, we can see that the Cement and Water are doing most of the “work” in fitting our suite of independent variables to our dependent variable of Compressive Strength.
Finally there is the P parameter for which the smaller it is, the better we can say that the relationship that we’ve made with our regression represents our dependent variable.
Now… on to looking at our results.
Here is where viewing the results of the regression is tricky.
We have 7 independent variables but we’d like to see the impact of the fit if all 7 variables on our strength
When I do this I like to plot the true y value against my regression y(x1,x2,x3,..)
So to do this I will take the fitted values of y and plot them against the original values of y
Getting the fitted values is easy.
I’m using the get_regression_points function which adds the modeled “y-hat” value to the dataframe of all of the other values get_regression_points() function.
The fitted version is the dependent variable w/ a "_hat"" at the end
fitted.S_v_all = get_regression_points(model = linear_model.S_v_all)
print(fitted.S_v_all)
NA
And finally we can plot our actual vs modeled values. (I’m adding a trend line)
# Making a simple X-Y scatterplot and adding a regression to it
ggplot(data = fitted.S_v_all) + # invoke graphics environment using a given dataframe
theme_bw( ) + # changing the plotting theme
aes(x = Compressive_Strength_28dy, # x-value
y = Compressive_Strength_28dy_hat) + # y-value
ggtitle("Yeh Superplasticizer Tests",
subtitle = "28-dy Compressive Strength (MPa)") + # EDITED: Custom Title now with a subtitle
ylab("Modelled") + # y-label
xlab("Observed") + # x-label
geom_point(colour="darkred") + # plot points
geom_smooth(method = "lm", # use a simple linar model
formula = y ~ x, # lm-style formula
se = TRUE, # display Confidence Intervals
level = 0.95, # Confidene Level to Map Out
colour = "red", # regression line color
fill = "magenta", # fill for confidence limits
size = 0.5) + # line thickness
geom_abline(slope = 1, # NEW: add a very simple line
intercept = 0, # (for a 1:1 reference)
color = "grey",
linetype = "dashed") +
coord_fixed(ratio = 1) # NEW: make the aspect ratio

# (I like my plots square)
And here we have a nice plot showing our true vs predicted values.
8. Regression Quality Metrics
And to close things off, we can do some general error metrics that may be useful..
First, the Mean Squared Error (MSE) or Bias… (if we are too high or too low)
\(BIAS = MSE = \frac{1}{N} \sum_{i=1}^{n} [\widehat{y}(\overrightarrow{x_i})-y_i] = \overline{[\widehat{y}(\overrightarrow{x_i})-y_i]}\)
# Calculate Bias (MSE)
bias = mean(fitted.S_v_all$Compressive_Strength_28dy_hat -
fitted.S_v_all$Compressive_Strength_28dy)
print(str_c(" Mean Squared Error (MSE) or Bias: ", bias))
[1] " Mean Squared Error (MSE) or Bias: 2.91262135922341e-05"
For a linear or multivariate regression the average of our residuals (the difference between each observation and prediction) should be zero.
The root mean squared error (RMSE) is shown here. It shouldn’t be zero since the residuals are squared before summing them up. We technically should use the standard error of the estimate, but RMSE remains a common error metric. We can always do both. The standard error of the estimate takes into account the degrees of freedom which which now includes all of the independent variables (p). We can get the standard error of the estimate from our
\(RMSE = \sqrt{ \frac{1}{N} \sum_{i=1}^{n} [\widehat{y}(\overrightarrow{x_i})-y_i]^2 } = \sqrt{\overline{[\widehat{y}(\overrightarrow{x_i})-y_i]^2} }\)
\(s_{e}\) or \(s_{y/x} = \sqrt{ \frac{1}{N-p-1} \sum_{i=1}^{n} [\widehat{y}(\overrightarrow{x_i})-y_i]^2 }\)
# Calculate RMSE
rmse = sqrt(mean( (fitted.S_v_all$Compressive_Strength_28dy_hat -
fitted.S_v_all$Compressive_Strength_28dy)^2) )
print(str_c(" Root Mean Squared Error (RMSE): ",
rmse))
[1] " Root Mean Squared Error (RMSE): 2.50527978593714"
print(str_c("Standard Error of the Estimate (se): ",
summary(linear_model.S_v_all)$sigma)) # you have to dig for this one!
[1] "Standard Error of the Estimate (se): 2.60865763395229"
And finally our correlation coefficient (which is basically our coefficient of determination before the “R” is “squared”)
# Get The Unadjusted Correlation Coefficient
r = cor(x = fitted.S_v_all$Compressive_Strength_28dy, # the x-value
y = fitted.S_v_all$Compressive_Strength_28dy_hat, # the y-value
method = "pearson" # method of correlation
)
print(str_c(" correlation coefficient (r): ", r))
[1] " correlation coefficient (r): 0.94701611900088"
print(str_c(" coefficient of determination (r²): ", r^2,
" ",
summary(linear_model.S_v_all)$r.squared))
[1] " coefficient of determination (r²): 0.896839529647489 0.896837609814009"
print(str_c("adjusted coefficient of determination (Adjusted r²): ",
summary(linear_model.S_v_all)$adj.r.squared))
[1] "adjusted coefficient of determination (Adjusted r²): 0.889236170537147"
And with that, we’re done… Once again, this exercise demonstrates a lot of tricks just to show how you can use R for various statistics. You may not use all of them in your encouters with R for linear or multivariate regression or even at all, but you may be able to cannibalize some of the tricks here for other applications.
LS0tCnRpdGxlOiAiVmlzdWFsaXppbmcgU3RhdGlzdGljcyBhbmQgUmVncmVzc2lvbnMgZnJvbSBhIFNwcmVhZHNoZWV0IHVzaW5nIFIiCm91dHB1dDoKICBwZGZfZG9jdW1lbnQ6IGRlZmF1bHQKICBodG1sX25vdGVib29rOgogICAgdG9jOiB5ZXMKICBodG1sX2RvY3VtZW50OgogICAgZGZfcHJpbnQ6IHBhZ2VkCiAgICB0b2M6IHllcwotLS0gCgpUaGlzIGlzIGFuIFtSIE1hcmtkb3duXShodHRwOi8vcm1hcmtkb3duLnJzdHVkaW8uY29tKSBOb3RlYm9vay4gV2hlbiB5b3UgZXhlY3V0ZSBjb2RlIHdpdGhpbiB0aGUgbm90ZWJvb2ssIHRoZSByZXN1bHRzIGFwcGVhciBiZW5lYXRoIHRoZSBjb2RlLiAKClRoZXNlIG5vdGVib29rcyBhcmUgdHlwaWNhbGx5IHRoaXMgaXMgZGVzaWduZWQgdG8gY3JlYXRlIGEgcGxlYXNpbmcgdmlld2luZyBlbnZpcm9ubWVudCBvZiBkYXRhIGFuYWx5c2lzIHRoYXQgYWxsb3dzIHlvdSB0byBpbmNsdWRlIGZpZ3VyZXMsIHRleHQsIGxpbmtzLCBldGMuIHNvIHRoYXQgeW91ciB3b3JrIGlzIGJldHRlciB1bmRlcnN0b29kIGFuZCBjYW4gYmUgcmVwcm9kdWNlZCBhbmQgdXNlZCB3aXRoIGNvbmZpZGVuY2UuCgpUaGUgc291cmNlIGNvZGUgZm9yIHRoaXMgUiBub3RlYm9vayAoUm1kIHN1ZmZpeGVkIGZpbGVzKSwgd2hlbiBzdG9yZWQgYXMgd2ViIHBhZ2VzIChodG1sIGZpbGVzKSwgY2FuIGJlIGRvd25sb2FkZWQgYnkgY2xpY2tpbmcgdGhlIGJ1dHRvbiBhdCB0aGUgdG9wIG9mIHRoZSBwYWdlLgoKSWYgdmlld2luZyB0aGUgc291cmNlIGNvZGUgaW4gUiBTdHVkaW8sIHRyeSBleGVjdXRpbmcgZWFjaCBSICJjaHVuayIgYnkgY2xpY2tpbmcgdGhlICpSdW4qIGJ1dHRvbiB3aXRoaW4gdGhlIGNodW5rIG9yIGJ5IHBsYWNpbmcgeW91ciBjdXJzb3IgaW5zaWRlIGl0IGFuZCBwcmVzc2luZyAqQ21kK1NoaWZ0K0VudGVyKi4gegoKCioqV2FybmluZy4gVHlwb3MgYXJlICpMZWdpb24qISoqCgojIDEuIEludHJvZHVjdGlvbgoKV2hlbiB5b3UncmUgaW4gW01BVEggMzgxIChJbnRybyB0byBQcm9iYWJpbGl0eSBhbmQgU3RhdHMpXShodHRwOi8vZWNhdGFsb2cuc2RzbXQuZWR1L3ByZXZpZXdfY291cnNlX25vcG9wLnBocD9jYXRvaWQ9MTcmY29pZD0yNjU3MSkgeW91J2xsIGdldCBhIHRhc3RlIG9mIFIuICBSIGlzIGFuIG9wZW4tc291cmNlIHN0YXRpc3RpY2FsIHBhY2thZ2UgYnVpbGQgb2ZmIG9mIGFuIGVhcmxpZXIgZ2VuZXJhdGlvbiBvZiBjb21tZXJjaWFsLgoKVGhlIGdvYWwgaGVyZSBpcyB0byBkZW1vbnN0cmF0ZSBjcmFja2luZyBvcGVuIGFuIGV4Y2VsIHNwcmVhZHNoZWV0IGluIFIgYW5kIGNhbGN1bGF0ZSBzb21lIGJhc2ljIHN0YXRzLCBjcmVhdGUgdmFyaW91cyBwbG90cyB0byB2aWV3IHRoZSBzdGF0aXN0aWNzLCBhbmQgZmluYWxseSwgZG8gc29tZSBsaW5lYXIgYW5kIG11bHRpdmFyaWF0ZSByZWdyZXNzaW9uCgpBbm90aGVyIGdvYWwgaGVyZSBpcyB0byBzaG93IG9mZiBzb21lIG9mIFIncyBmZWF0dXJlcy4gIFIgaXMgYSB2ZXJ5IHBvd2VyZnVsIHRvb2wuICBXaGVuIHRyYW5zbGF0aW5nICJwb3dlcmZ1bCIgZnJvbSBjb21wdXRlcmVlc2UgdG8gYW55IGZydXN0cmF0ZWQgaHVtYW4gZGlhbGVjdCwgdGhhdCBtZWFucyAic3RlZXAgbGVhcm5pbmcgY3VydmUuIiAgSXQncyBhbHNvIGEgY29tbXVuaXR5LXN1cHBvcnRlZCBlbnZpcm9ubWVudC4gIFdoZW4gdHJhbnNsYXRpbmcgInBvd2VyZnVsIiBmcm9tIGNvbXB1dGVyZWVzZSB0byBhbnkgb3ZlcnNjaGVkdWxlZCBodW1hbiBkaWFsZWN0LCB0aGF0IG1lYW5zICJ0aGVyZSBhcmUgTE9UUyBvZiBwZW9wbGUgZG9uYXRpbmcgcGFja2FnZXMgYW5kIGxpYnJhcmllcyB0byBSLiIgIFNvbWUgaGF2ZSBldm9sdmVkIHRvIGJlIGEgc3RhbmRhcmQgaW4gdGhlIGNvbW11bml0eS4gIE90aGVycyBhcmUgaGlnaGx5IHNwZWNpYWxpemVkIGZvciBhIGdpdmVuIGRpc2NpcGxpbmUgKGJ1dCBoYXZlIG9uZSBvciB0d28gaXRlbXMgdGhhdCBwZW9wbGUgb3V0c2lkZSB0aGVpciB1c2VyIGNvbW11bml0aWVzIGZpbmQgaGFuZHkuKQoKQnV0IGRvbid0IGxldCB0aGF0IGludGltaWRhdGUgeW91LiAgT25jZSB5b3UgbGVhcm4gb25lIGxhbmd1YWdlIHlvdSBjYW4gc2xvd2x5IHBpY2sgdXAgbW9yZS4gIEFsc28gd2l0aCB0aGlzIGRlbW8gd2UgYXJlbid0IGdvaW5nIHRvIGdldCB0byB0byBiZSBhbiBSIGd1cnUgaW4gYSBkYXkuIAoKSWYgeW91IHdhbnQgYSBnb29kIHN0ZXBwaW5nIG9mZiBwb2ludCB0byBsZWFybiBSIEknZCByZWNvbW1lbmQgc29tZSBvZiB0aGUgcmVzb3VyY2VzIGF0IFtEYXRhIENhbXBdKGh0dHBzOi8vd3d3LmRhdGFjYW1wLmNvbS9jb3Vyc2VzL2ZyZWUtaW50cm9kdWN0aW9uLXRvLXIpIHdoaWNoIGhhdmUgc29tZSBmcmVlIHN0YXJ0ZXIgdHV0b3JpYWxzIGZvciBSLgoKCiMgMi4gTG9hZGluZyB0aGUgTGlicmFyaWVzCgpUbyB3b3JrIHdpdGggUiB3ZSB3aWxsIGZpcnN0IGhhdmUgdG8gbG9hZCBzb21lIGxpYnJhcmllcy4gIFRoaXMgaXMgbGlrZSBpbiBDIHdoZXJlIHlvdSBoYXZlIHRoZSAjaW5jbHVkZSBzdGF0ZW1lbnQgdG8gZG8gdGhpbmdzIGxpa2UgcmFpc2UgdGhpbmdzIHRvIHBvd2VycyBhbmQgc3R1ZmYgbGlrZSB0aGF0LgoKU29tZSBvZiB0aGVzZSBsaWJyYXJpZXMgb3IgInBhY2thZ2VzIiBjb21lIHdpdGggUi4gIE90aGVycyB3aWxsIGhhdmUgdG8gYmUgaW5zdGFsbGVkLiAgSGVyZSBhcmUgdGhlIG9uZXMgd2UgYXJlIHVzaW5nIGZvciB0aGlzIGV4ZXJjaXNlLgoKQWxzbyBpbiB0aGlzIGV4ZXJjaXNlLCB3ZSdyZSBnb2luZyB0byB1c2UgdGhlIFt0aWR5dmVyc2VdKGh0dHBzOi8vd3d3LnRpZHl2ZXJzZS5vcmcpIHNldCBvZiBwYWNrYWdlcy4gIFRpZHl2ZXJzZSBpcyBhIHNldCBvZiBjby1kZXZlbG9wZWQgdG9vbHMgZm9yIGRhdGEgc2NpZW5jZSBpbiBSLiAgVGhpcyBpcyB0aGUgbmV3IGJpZyB0aGluZyBpbiBSIGFuZCBpcyB3aWRlbHkgdXNlZCBzbyB3ZSBhcmUganVzdCBnb2luZyB0byBqdW1wIGluIGhlcmUuICBTRCBNaW5lcyBoYXMgYSBjb3Vyc2UgYmV5b25kIEVuZ2luZWVyaW5nIFN0YXRzLCBbTUFUSCA0NDMvNTQzIChEYXRhIEFuYWx5c2lzKV0oaHR0cDovL2VjYXRhbG9nLnNkc210LmVkdS9wcmV2aWV3X2NvdXJzZV9ub3BvcC5waHA/Y2F0b2lkPTE3JmNvaWQ9MjY5NzMpIHRoYXQgbGV2ZXJhZ2VzIHRoaXMgc2V0IG9mIHBhY2thZ2VzLgoKKiBJbnN0YWxsIFVzIEZpcnN0CiAgKyBbdGlkeXZlcnNlXShodHRwczovL3d3dy50aWR5dmVyc2Uub3JnKSA6IFNldCBvZiBjb21tb25seS11c2VkIERhdGEgU2NpZW5jZSBwYWNrYWdlcyBmb3IgUiB0aGF0IGl0IGNhbiBpbnN0YWxsIGFuZCBsb2FkIGFsbCBhdCBvbmNlLiBJbiB0aGUgbG9uZy1ydW4geW91IHByb2JhYmx5IGFsc28gd2FudCB0byBpbnN0YWxsIHRoZSB0aWR5dmVyc2UgcGFja2FnZSBzdWl0ZSBhbnl3YXkuIEZvciB0aGlzIGV4ZXJjaXNlIHRoaXMgd2lsbCBpbmNsdWRlLi4uICAgCiAgICAtIFtnZ3Bsb3QyXShodHRwczovL2dncGxvdDIudGlkeXZlcnNlLm9yZykgOiBDcmVhdGUgRWxlZ2FudCBEYXRhIFZpc3VhbGl6YXRpb25zIFVzaW5nIHRoZSBHcmFtbWFyIG9mIEdyYXBoaWNzCiAgICAtIFt0aWJibGVdKGh0dHBzOi8vdGliYmxlLnRpZHl2ZXJzZS5vcmcpIDogU2ltcGxlIERhdGEgRnJhbWVzCiAgICAtIFt0aWR5cl0oaHR0cHM6Ly90aWR5ci50aWR5dmVyc2Uub3JnKSA6IFRvb2xzIGZvciBzaGVwaGVyZGluZyBkYXRhIGluIGRhdGEgZnJhbWVzLgogICAgLSBbcmVhZHJdKGh0dHBzOi8vcmVhZHIudGlkeXZlcnNlLm9yZykgOiBSZWFkIFJlY3Rhbmd1bGFyIFRleHQgRGF0YQogICAgLSBbcHVycl0oaHR0cHM6Ly9wdXJyci50aWR5dmVyc2Uub3JnKSA6IEZ1bmN0aW9uYWwgUHJvZ3JhbW1pbmcgVG9vbHMKICAgIC0gW2RwbHlyXShodHRwczovL2RwbHlyLnRpZHl2ZXJzZS5vcmcpIDogQSBncmFtbWFyIG9mIGRhdGEgbWFuaXB1bGF0aW9uCiAgICAtIFtzdHJpbmdyXShodHRwczovL3N0cmluZ3IudGlkeXZlcnNlLm9yZykgOiBTaW1wbGUsIENvbnNpc3RlbnQgV3JhcHBlcnMgZm9yIENvbW1vbiBTdHJpbmcgT3BlcmF0aW9ucwogICAgLSBbZm9yY2F0c10oaHR0cHM6Ly9mb3JjYXRzLnRpZHl2ZXJzZS5vcmcpIDogVG9vbHMgZm9yIFdvcmtpbmcgd2l0aCBDYXRlZ29yaWNhbCBWYXJpYWJsZXMgKEZhY3RvcnMpCgogICsgW3JlYWR4bF0oaHR0cHM6Ly93d3cucmRvY3VtZW50YXRpb24ub3JnL3BhY2thZ2VzL3JlYWR4bC92ZXJzaW9ucy8xLjEuMCkgOiBhbHNvIHBhcnQgb2YgdGhlIFt0aWR5dmVyc2VdKGh0dHBzOi8vd3d3LnRpZHl2ZXJzZS5vcmcpIHBhY2thZ2Ugc3VpdGUgZm9yIHJlYWRpbmcgdHJhZGl0aW9uYWwgZXhjZWwgc3ByZWFkc2hlZXRzLiAgCiAgKyBbbW9kZXJuZGl2ZV0oVGlkeXZlcnNlLUZyaWVuZGx5IEludHJvZHVjdG9yeSBMaW5lYXIgUmVncmVzc2lvbikgOiBUaWR5dmVyc2UtRnJpZW5kbHkgSW50cm9kdWN0b3J5IExpbmVhciBSZWdyZXNzaW9uCgogIAoqIFRoaXMgc2hvdWxkIGNvbWUgd2l0aCBSJ3MgY29yZSBpbnN0YWxsLCBpZiBub3QgaW5zdGFsbCAnZW0uCiAgKyBbTUFTU10oaHR0cHM6Ly93d3cucmRvY3VtZW50YXRpb24ub3JnL3BhY2thZ2VzL01BU1MvdmVyc2lvbnMvNy4zLTUwKSA6IEhhcyBhIGxvdCBvZiByZXNvdXJjZXMgZm9yIHJlZ3Jlc3Npb24uCgoqIFRoaXMgZG9lc24ndCBjb21lIHdpdGggUidzIGNvcmUgaW5zdGFsbCBzbyBpbnN0YWxsIHRoYXQgb25lLi4uIAogICsgW21vbWVudHNdKGh0dHBzOi8vd3d3LnJkb2N1bWVudGF0aW9uLm9yZy9wYWNrYWdlcy9tb21lbnRzL3ZlcnNpb25zLzAuMTQpIDogVGhpcyBoYXMgYSBsb2FkIG9mIGdvb2Qgc3R1ZmYgZm9yIGRhdGEgYW5hbHlzaXMgYW5kIHBsb3R0aW5nLCBtb3JlIHRoYW4geW91IHdpbGwgbmVlZCBoZXJlLCBidXQgZ2V0IGl0IGFueXdheS4KCiogVGhpcyBpcyBhIG5pY2UgY29udHJpYnV0ZWQgbGlicmFyeSB0aGF0IGxldHMgdXMgbWFrZSBwcmV0dHkgc3RhdGlzdGljcyB0YWJsZXMuICBJdCB3YXMgd3JpdHRlbiBmb3IgZWNvbG9naWNhbCBhcHBsaWNhdGlvbnMgYnV0IGl0J3Mgc3RpbGwgcHJldHR5IGhhbmR5IGZvciBsb29raW5nIGF0IGNvbmNyZXRlCiAgKyBbcGFzdGVjc10oaHR0cHM6Ly93d3cucmRvY3VtZW50YXRpb24ub3JnL3BhY2thZ2VzL3Bhc3RlY3MvdmVyc2lvbnMvMS4zLjIxKTogUGFja2FnZSBmb3IgQW5hbHlzaXMgb2YgU3BhY2UtVGltZSBFY29sb2dpY2FsIFNlcmllcwogIAoqIEFub3RoZXIgbmljZSBjb250cmlidXRlZCBsaWJyYXJ5IHRoYXQgbWFrZXMgbWF0cmljZXMgb2YgY29ycmVsYXRpb24gY29lZmZpY2llbnRzIGxvb2sgcHJldHR5IChhbmQgIGdyYXBoaWNhbGx5IGluZm9ybWF0aXZlKS4KICArIFtjb3JycGxvdF0oaHR0cHM6Ly93d3cucmRvY3VtZW50YXRpb24ub3JnL3BhY2thZ2VzL2NvcnJwbG90L3ZlcnNpb25zLzAuODQpIFZpc3VhbGl6YXRpb24gb2YgYSBDb3JyZWxhdGlvbiBNYXRyaXgKCiogV2hpbGUgbm90IG9mZmljaWFsbHkgbmVlZGVkIGZvciB0aGlzIGFjdGl2aXR5IGJ1dCBJJ2xsIGRlbW9uc3RyYXRlIGhvdyB1bml0cyBjYW4gYmUgdXNlZCBpbiBSIGluIHRoaXMgZXhhbXBsZQogICsgW3VkdW5pdHMyXShodHRwczovL3d3dy5yZG9jdW1lbnRhdGlvbi5vcmcvcGFja2FnZXMvdWR1bml0czIvdmVyc2lvbnMvMC4xMykgUHJvdmlkZXMgc2ltcGxlIGJpbmRpbmdzIHRvIFVuaWRhdGEncyB1ZHVuaXRzIGxpYnJhcnkgZm9yIHVuaXQgY29udmVyc2lvbnMgKHdpbGwgYmUgZGVtb25zdHJhdGluZyBidXQgbm90IGV4cGxpY2l0eSBuZWVkaW5nIGl0IGhlcmUpCiAgKyBbdW5pdHNdKGh0dHBzOi8vd3d3LnJkb2N1bWVudGF0aW9uLm9yZy9wYWNrYWdlcy91bml0cy92ZXJzaW9ucy8wLjYtMCkgUHJvdmlkZXMgTWVhc3VyZW1lbnQgVW5pdHMgZm9yIFIgVmVjdG9ycwoKYGBge3J9CgogICMgVGlkeXZlcnNlIEhhbmRsaW5nIExpYnJhcmllcwoKICAgIApsaWJyYXJ5KHBhY2thZ2UgPSAidGlkeXZlcnNlIikgICMgbWFpbiB0aWR5dmVyc2Ugc3VpdGUKICBsaWJyYXJ5KHBhY2thZ2UgPSAicmVhZHhsIikgICAgICMgUmVhZCBFeGNlbCBGaWxlcwogIGxpYnJhcnkocGFja2FnZSA9ICJtb2Rlcm5kaXZlIikgIyByZWdyZXNzaW9uIHN1cHBvcnQKCiAgIyBTdGF0aXN0aWNzIExpYnJhcmllcwoKICBsaWJyYXJ5KHBhY2thZ2UgPSAibW9tZW50cyIpICAgIyBNb21lbnRzLCBjdW11bGFudHMsIHNrZXduZXNzLCBrdXJ0b3NpcyBhbmQgcmVsYXRlZCB0ZXN0cwogIGxpYnJhcnkocGFja2FnZSA9ICJNQVNTIikgICAgICAjIFN1cHBvcnQgRnVuY3Rpb25zIGFuZCBEYXRhc2V0cyBmb3IgVmVuYWJsZXMgJiBSaXBsZXkncyBNQVNTIHRleHQKCiAgIyBFeHRyYSBHcmFwaGljcyBMaWJyYXJpZXMKCiAgbGlicmFyeShwYWNrYWdlID0gImNvcnJwbG90IikgICMgVmlzdWFsaXphdGlvbiBvZiBhIENvcnJlbGF0aW9uIE1hdHJpeAoKCiAgIyBEYXRhIFByb2Nlc3NpbmcgTGlicmFyaWVzCgogIGxpYnJhcnkocGFja2FnZSA9ICJwYXN0ZWNzIikgICAjIFBhY2thZ2UgZm9yIEFuYWx5c2lzIG9mIFNwYWNlLVRpbWUgRWNvbG9naWNhbCBTZXJpZXMKCiAgbGlicmFyeShwYWNrYWdlID0gInVkdW5pdHMyIikgICMgVW5pdCBDb252ZXJzaW9uIFN1cHBvcnQKICBsaWJyYXJ5KHBhY2thZ2UgPSAidW5pdHMiKSAgICAgIyBNZWFzdXJlbWVudCBVbml0cyBmb3IgUiBWZWN0b3JzCgpgYGAKCiMgMy4gQ3JhY2tpbmcgYSBTcHJlYWRzaGVldAoKVGhlIHNwcmVhZHNoZWV0IGV4YW1wbGUgYmVsb3cgaXMgYSBtb3JlIGNvbXBsaWNhdGVkIHRoYW4gd2hhdCB5b3UgaG9wZWZ1bGx5IGhhdmUuCgpUaGUgb3JpZ2luYWwgZGF0YSBzZXQgaXMgZnJvbSBhIHNldCBvZiBwYXBlcnMgb24gQ29uY3JldGUgYnkgSS1DaGVuZyBZZWggCgoqIFtZZWgsIEktQ2hlbmcsICJNb2RlbGluZyBzbHVtcCBvZiBjb25jcmV0ZSB3aXRoIGZseSBhc2ggYW5kIHN1cGVycGxhc3RpY2l6ZXIsIiAqQ29tcHV0ZXJzIGFuZCBDb25jcmV0ZSosICoqNSoqKDYpLCA1NTktNTcyLCAyMDA4LiBkb2k6IDEwLjEyOTg5L2NhYy4yMDA4LjUuNi41NTkuXShodHRwOi8vd3d3LnRlY2huby1wcmVzcy5vcmcvY29udGVudC8/cGFnZT1hcnRpY2xlJmpvdXJuYWw9Y2FjJnZvbHVtZT01Jm51bT02Jm9yZGVybnVtPTQpCgoqIFtZZWgsIEktQ2hlbmcsICJTaW11bGF0aW9uIG9mIGNvbmNyZXRlIHNsdW1wIHVzaW5nIG5ldXJhbCBuZXR3b3JrcywiICpDb25zdHJ1Y3Rpb24gTWF0ZXJpYWxzKiwgKioxNjIqKigxKSwgMTEtMTgsIDIwMDkuIGRvaTogMTAuMTY4MC9jb21hLjIwMDkuMTYyLjEuMTFdKGh0dHBzOi8vd3d3LmljZXZpcnR1YWxsaWJyYXJ5LmNvbS9kb2kvMTAuMTY4MC9jb21hLjIwMDkuMTYyLjEuMTEpCgoqIFtZZWgsIEktQ2hlbmcsICJQcmVkaWN0aW9uIG9mIHdvcmthYmlsaXR5IG9mIGNvbmNyZXRlIHVzaW5nIGRlc2lnbiBvZiBleHBlcmltZW50cyBmb3IgbWl4dHVyZXMsIiAqQ29tcHV0ZXJzIGFuZCBDb25jcmV0ZSosICoqNSoqKDEpLCAxLTIwLCAyMDA4LiBkb2k6IDEwLjEyOTg5L2NhYy4yMDA4LjUuMS4wMDFdKGh0dHA6Ly93d3cudGVjaG5vLXByZXNzLm9yZy9jb250ZW50Lz9wYWdlPWFydGljbGUmam91cm5hbD1jYWMmdm9sdW1lPTUmbnVtPTEmb3JkZXJudW09MSkKCiogW1llaCwgSS1DaGVuZywgIk1vZGVsaW5nIHNsdW1wIGZsb3cgb2YgY29uY3JldGUgdXNpbmcgc2Vjb25kLW9yZGVyIHJlZ3Jlc3Npb25zIGFuZCBhcnRpZmljaWFsIG5ldXJhbCBuZXR3b3JrcywiICpDZW1lbnQgYW5kIENvbmNyZXRlIENvbXBvc2l0ZXMqLCAqKjI5KiooNiksIDQ3NC00ODAsIDIwMDcuIGRvaTogMTAuMTAxNi9qLmNlbWNvbmNvbXAuMjAwNy4wMi4wMDFdKGh0dHBzOi8vd3d3LnNjaWVuY2VkaXJlY3QuY29tL3NjaWVuY2UvYXJ0aWNsZS9waWkvUzA5NTg5NDY1MDcwMDAyNjE/dmlhJTNEaWh1YikKCiogW1llaCwgSS1DaGVuZywgIkV4cGxvcmluZyBjb25jcmV0ZSBzbHVtcCBtb2RlbCB1c2luZyBhcnRpZmljaWFsIG5ldXJhbCBuZXR3b3JrcywiICpBU0NFIEouIG9mIENvbXB1dGluZyBpbiBDaXZpbCBFbmdpbmVlcmluZyosICoqMjAqKigzKSwgMjE3LTIyMSwgMjAwNi4gZG9pOiAxMC4xMDYxLyhBU0NFKTA4ODctMzgwMSgyMDA2KTIwOjMoMjE3KV0oaHR0cHM6Ly9hc2NlbGlicmFyeS5vcmcvZG9pLzEwLjEwNjEvJTI4QVNDRSUyOTA4ODctMzgwMSUyODIwMDYlMjkyMCUzQTMlMjgyMTclMjkpCgphbmQgaXMga2VwdCBhdCB0aGUgW1VDLUlydmluZSBNYWNoaW5lIExlYXJuaW5nIFJlcG9zaXRvcnldKGh0dHBzOi8vYXJjaGl2ZS5pY3MudWNpLmVkdS9tbC9kYXRhc2V0cy9Db25jcmV0ZStTbHVtcCtUZXN0KS4KCgoKSXQgY2FuIGJlIGZvdW5kIGhlcmUgYXQgW2h0dHA6Ly9reXJpbGwuaWFzLnNkc210LmVkdS9jZWVfMjg0L0Jhc2VfQ29uY3JldGVfU2x1bXBfVGVzdF9mb3JfUi54bHN4XShodHRwOi8va3lyaWxsLmlhcy5zZHNtdC5lZHUvY2VlXzI4NC9CYXNlX0NvbmNyZXRlX1NsdW1wX1Rlc3RfZm9yX1IueGxzeCkKClRoZSByZWxldmFudCBwYWdlIGFuZCBzY3JlZW5zaG90IGlzIGJlbG93LiAgRm9yIGRyYW1hLWZyZWUgUiBpbXBvcnQgeW91IGFyZSBwcm9iYWJseSBiZXN0IG9mZiBrZWVwaW5nIGEgcGFnZSBvbiB5b3VyIHNwcmVhZHNoZWV0IGZpbGUgdGhhdCBpcyB2ZXJ5IHNpbXBsZSwgd2l0aCBudW1iZXJzIGdvaW5nIGRvd24sIGFuZCBhIHNpbmdsZSBsaW5lIGZvciBSb3ctMSB3aXRoIHRoZSBoZWFkZXJzIG9mIGVhY2ggY29sdW1uLiAgSWYgeW91IHdhbnQgdG8gZ2V0IGZhbmN5IG9uIG90aGVyIHBhZ2VzIHRoYXQgeW91J2QgdHVybiBpbiBhcyB0YWJsZXMgaW4gcmVwb3J0cywgeW91IGNhbiBkbyB0aGF0IG9uIGFub3RoZXIgc3ByZWFkc2hlZXQgcGFnZS4KCiFbQ29uY3JldGUgU3ByZWFkc2hlZXQgU2NyZWVuc2hvdF0oaHR0cDovL2t5cmlsbC5pYXMuc2RzbXQuZWR1L3dqYy9lZHVyZXNvdXJjZXMvQmFzZV9Db25jcmV0ZV9TbHVtcF9UZXN0X2Zvcl9SLnBuZykKClRvIGNyYWNrIG9wZW4gdGhlIHNwcmVhZHNoZWV0IHdlIHdpbGwgd2FudCB0byB1c2UgdGhlIFtyZWFkX2V4Y2VsXShodHRwczovL3d3dy5yZG9jdW1lbnRhdGlvbi5vcmcvcGFja2FnZXMvcmVhZHhsL3ZlcnNpb25zLzEuMS4wL3RvcGljcy9yZWFkX2V4Y2VsKSBmdW5jdGlvbi4KCllvdSBjYW4gcmVhZCB0aGUgc3ByZWFkc2hlZXQgZnJvbSBhIGxvY2FsIGRyaXZlIG9yIGZyb20gYSB3ZWJzaXRlLgoKYGBge3J9CgogICMgeW91IHdpbGwgbmVlZCB0aGUgZnVsbCBwYXRoIHRvIHRoZSBmaWxlIHlvdSBhcmUgdXNpbmcgKGVpdGhlciBvbmxpbmUgb3IgbG9jYWxseSBvbiB5b3VyIGRpc2spCgogICMgVGhlIGlmIGVsc2UgYmxvY2sgc2hvdWxkIHF1ZXJ5IHlvdXIgbWFjaGluZSB0byBkZXRlcm1pbmUgd2hpY2ggb3BlcmF0aW5nIHN5c3RlbS4KICAjICBpZiB5b3UgYXJlIG5vdCBiaS1wbGF0Zm9ybSwgeW91IGxpa2VseSBkb24ndCBuZWVkIHRoaXMuCgogIGlmKC5QbGF0Zm9ybSRPUy50eXBlID09ICJ3aW5kb3dzIikgewogICAgIyBXaW5kb3dzCiAgICBzcHJlYWRzaGVldF9uYW1lICAgICA9ICIlSE9NRVBBVEglL0Rvd25sb2Fkcy9CYXNlX0NvbmNyZXRlX1NsdW1wX1Rlc3RfZm9yX1IueGxzeCIKICB9IGVsc2UgewogICAgIyBVbml4IChMaW51eCwgTWFjT1MsIFNvbGFyaXMpCiAgICBzcHJlYWRzaGVldF9uYW1lICAgICA9ICJ+L0Rvd25sb2Fkcy9CYXNlX0NvbmNyZXRlX1NsdW1wX1Rlc3RfZm9yX1IueGxzeCIKICB9CgoKICAjIEkgYW0ga2VlcGluZyBhIGNvcHkgb2YgdGhlc2Ugc3ByZWFkc2hlZXQgYXQgdGhlIFVSTCBiZWxvdy4gIEl0IGNhbiBiZSBkb3dubG9hZGVkIGF1dG9tYXRpY2FsbHkKICAjICAgYW5kIHRoZW4gbG9hZGVkLiAgV2UgY2FuIGFsc28gZGlzY3JldGVseSBkZWxldGUgaXQgd2hlbiBkb25lLgoKICAgICAgc3ByZWFkc2hlZXRfdXJsID0gImh0dHA6Ly9reXJpbGwuaWFzLnNkc210LmVkdS93amMvZWR1cmVzb3VyY2VzL0Jhc2VfQ29uY3JldGVfU2x1bXBfVGVzdF9mb3JfUi54bHN4IgogICAKICAgICAgZG93bmxvYWQuZmlsZSh1cmwgICAgICA9ICAgc3ByZWFkc2hlZXRfdXJsLCAjIFVSTCBsb2NhdGlvbgogICAgICAgICAgICAgICAgICAgIGRlc3RmaWxlID0gc3ByZWFkc2hlZXRfbmFtZSkgIyBsb2NhbCBkb3dubG9hZGVkIGxvY2F0aW9uCiAgICAgIAogICAgICByZW1vdmUoc3ByZWFkc2hlZXRfdXJsKSAjIGNsZWFuIHVwIHZhcmlhYmxlcwogIAogICMgdGhpcyBjb21tYW5kIHdpbGwgcmVhZCB0aGUgZmlsZQoKICBjb25jcmV0ZSA9IHJlYWRfZXhjZWwocGF0aCAgICAgID0gc3ByZWFkc2hlZXRfbmFtZSwgICMgcmVtb3ZlIHNwcmVhZHNoZWV0IGxvY2F0aW9uCiAgICAgICAgICAgICAgICAgICAgICAgIHNoZWV0ICAgICA9ICJEYXRhIiwgICAgICAgICAgICAjIHBhZ2Ugb2Ygc3ByZWFkc2hlZXQKICAgICAgICAgICAgICAgICAgICAgICAgY29sX25hbWVzID0gVFJVRSkgICAgICAgICAgICAgICMgZmlyc3Qgcm93IGFyZSB0aGUgY29sdW1uIGhlYWRlcnMKICAKICAKICAjIGNsZWFuIHVwIHlvdXIgaGFyZCBkcml2ZSEgIERvbid0IGJlIGxpa2UgbWUhCgogIGlmKC5QbGF0Zm9ybSRPUy50eXBlID09ICJ3aW5kb3dzIikgewogICAgIyBXaW5kb3dzCiAgICBzeXN0ZW0oc3RyX2MoIkRFTCAgICIsIAogICAgICAgICAgICAgICAgIHNwcmVhZHNoZWV0X25hbWUsCiAgICAgICAgICAgICAgICAgc2VwPSIiKSkKICAgIH0gZWxzZSB7CiAgICAjIFVuaXggKExpbnV4LCBNYWNPUywgU29sYXJpcykKICAgIHN5c3RlbShzdHJfYygicm0gLXYgICIsIAogICAgICAgICAgICAgICAgIHNwcmVhZHNoZWV0X25hbWUsCiAgICAgICAgICAgICAgICAgc2VwPSIiKSkKICAgICAgfQogIAogIHJlbW92ZShzcHJlYWRzaGVldF9uYW1lKSAjIGNsZWFuIHVwIHZhcmlhYmxlcwoKICAKYGBgCgpXaXRoIHRoZSBkYXRhIHJlYWQgaW4gd2UgY2FuIG5vdyBsb29rIGF0IHRoZSB0YWJsZSBvZiB0aGUgZGF0YS4gIFRoaXMgbG9va3MgbXVjaCBuaWNlciB3aGVuIHdvcmtpbmcgaW4gUiBOb3RlYm9va3MgaW5zdGVhZCBvZiBQbGFpbiBPcmRpbmFyeSBSLgoKYGBge3J9CgogICMgUHJpbnQgZGF0YSBmcmFtZQogIGNvbG5hbWVzKGNvbmNyZXRlKVsxXSA9ICJUZXN0X051bWJlciIKICBwcmludChjb25jcmV0ZSkKCmBgYAojIyMgRXh0cmE6IFVuaXRzIChub3QgcGFydCBvZiB0aGlzIGV4ZXJjaXNlIGJ1dCBpdCdzIGEgbmlmdHkgdGFuZ2VudCkKCipEYW5nLiAgSSBsaWtlIHVuaXRzLiBJIGRvbid0IHNlZSBhbnkuICBJJ20gYW5hbCBhbmQgaGF2ZSBsZWFybmVkIHRoYXQgYWRkaW5nIGFzIG11Y2ggZGVzY3JpcHRpdmUgZGF0YSBlYXJseSBvbiBpbiBwcm9jZXNzaW5nIHlvdXIgZGF0YSBzZXQgd2lsbCBtYWtlIHBlb3BsZSAoYW5kIG1vc3QgaW1wb3J0YW50bHksIHlvdXJzZWxmKSBub3QgaGF0ZSB5b3UgYXQgYSBsYXRlciBkYXRlLiAgU28gSSBhbSBhZGRpbmcgdGhlbSBoZXJlIHdpdGggdGhlIFtzZXRfdW5pdHNdKGh0dHBzOi8vd3d3LnJkb2N1bWVudGF0aW9uLm9yZy9wYWNrYWdlcy91bml0cy92ZXJzaW9ucy8wLjYtMC90b3BpY3Mvc2V0X3VuaXRzKSBmdW5jdGlvbi4gVGhpcyB3aWxsIGFkZCB1bml0cyBhcyBhbiBhdHRyaWJ1dGUuCgpVbml0cyBkb24ndCB3b3JrIHdpdGggZXZlcnl0aGluZyBhbmQgeW91IHNob3VsZCBwcm9iYWJseSBrZWVwIGEgY29weSBvZiB5b3VyIG9yaWdpbmFsIHVuLXVuaXR0ZWQgZGF0YSBmcmFtZS4gIAoKCmBgYHtyfQoKIyBmaXJzdCB3ZSBjbG9uZSBvdXIgZGF0YSBmcmFtZQoKY29uY3JldGVfdW5pdHMgPSBjb25jcmV0ZQoKY29uY3JldGVfdW5pdHMkQ2VtZW50ICAgICAgICAgICAgICAgICAgICA9IHNldF91bml0cyh4ICAgICA9IGNvbmNyZXRlX3VuaXRzJENlbWVudCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFsdWUgPSAia2cgbS0zIikKCmNvbmNyZXRlX3VuaXRzJFNsYWcgICAgICAgICAgICAgICAgICAgICAgPSBzZXRfdW5pdHMoeCAgICAgPSBjb25jcmV0ZV91bml0cyRTbGFnLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YWx1ZSA9ICJrZyBtLTMiKQoKY29uY3JldGVfdW5pdHMkRmx5X0FzaCAgICAgICAgICAgICAgICAgICA9IHNldF91bml0cyh4ICAgICA9IGNvbmNyZXRlX3VuaXRzJEZseV9Bc2gsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhbHVlID0gImtnIG0tMyIpCgpjb25jcmV0ZV91bml0cyRXYXRlciAgICAgICAgICAgICAgICAgICAgID0gc2V0X3VuaXRzKHggICAgID0gY29uY3JldGVfdW5pdHMkV2F0ZXIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhbHVlID0gImtnIG0tMyIpCgpjb25jcmV0ZV91bml0cyRTdXBlcnBsYXN0aWNpemVyICAgICAgICAgID0gc2V0X3VuaXRzKHggICAgID0gY29uY3JldGVfdW5pdHMkU3VwZXJwbGFzdGljaXplciwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFsdWUgPSAia2cgbS0zIikKCmNvbmNyZXRlX3VuaXRzJENvYXJzZV9BZ2dyZWdhdGVzICAgICAgICAgPSBzZXRfdW5pdHMoeCAgICAgPSBjb25jcmV0ZV91bml0cyRDb2Fyc2VfQWdncmVnYXRlcywgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFsdWUgPSAia2cgbS0zIikKCmNvbmNyZXRlX3VuaXRzJEZpbmVfQWdncmVnYXRlcyAgICAgICAgICAgPSBzZXRfdW5pdHMoeCAgICAgPSBjb25jcmV0ZV91bml0cyRGaW5lX0FnZ3JlZ2F0ZXMsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhbHVlID0gImtnIG0tMyIpCgpjb25jcmV0ZV91bml0cyRTbHVtcCAgICAgICAgICAgICAgICAgICAgID0gc2V0X3VuaXRzKHggICAgID0gY29uY3JldGVfdW5pdHMkU2x1bXAsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhbHVlID0gImNtIikKCmNvbmNyZXRlX3VuaXRzJEZsb3cgICAgICAgICAgICAgICAgICAgICAgPSBzZXRfdW5pdHMoeCAgICAgPSBjb25jcmV0ZV91bml0cyRGbG93LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YWx1ZSA9ICJjbSIpCgpjb25jcmV0ZV91bml0cyRDb21wcmVzc2l2ZV9TdHJlbmd0aF8yOGR5ID0gc2V0X3VuaXRzKHggICAgID0gY29uY3JldGVfdW5pdHMkQ29tcHJlc3NpdmVfU3RyZW5ndGhfMjhkeSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFsdWUgPSAiTVBhIikKCnByaW50KGNvbmNyZXRlX3VuaXRzKQoKCmBgYAoKSWYgeW91IGNsaWNrIGluIHRoZSBHbG9iYWwgRW52aXJvbm1lbnQgQm94LCB0aG9zZSB1bml0cyBhcmVuJ3QgYXJiaXRyYXJ5IHN0cmluZ3MuIFRoZXkgYXJlIGxpc3RlZCBhcyBudW1lcmF0b3JzLCBkZW5vbWluYXRvcnMgYW5kIGFsc28gdGhlIHdheSBpbiB3aGljaCBzcXVhcmVzLCBldGMuLCBhcmUgYXJjaGl2ZWQgYXJlIGV4cGxpY2l0LgoKQmV0dGVyIFN0aWxsLCB0aGUgc2FtZSBjb21tYW5kIG9mIHNldF91bml0cyB3aGVuIGFwcGxpZWQgdG8gYSB2YXJpYWJsZSB0aGF0IGFscmVhZHkgaGFzIHVuaXRzIHdpbGwgY29udmVydCBpdC4gIFRoaXMgaXMgbmljZSB3aGVuIG1vdmluZyBiZXR3ZWVuIFNJIHVuaXRzLCBVU0NTIHVuaXRzLiAgW0lmIHlvdSBhcmUgZ29pbmcgdG8gYmUgY2hlZWt5IGFuZCB0cnkgdGhlIEZ1cmxvbmcvRmlya2luL0ZvcnRuaWdodCBzeXN0ZW0gKEZGRiksIHNvcnJ5IHRvIGRpc2FwcG9pbnQsIHRoYXQgd2hpbGUgdGhlIHVkdW5pdHMyIHBhY2thZ2UgaW4gUiByZWNvZ25pemVzIGFsbCB0aHJlZSB1bml0cywgaXQgcmVjb2duaXplcyBmaXJraW5zIGFzIGEgdm9sdW1lIG1lYXN1cmUgKHdoaWNoIGlzIHJlYWxseSBpcykgYW5kIG5vdCB0aGUgbWFzcyBtZWFzdXJlIGJhc2VkIG9uIGRlbnNpdHkgb2Ygd2F0ZXIuXQoKRXhhbXBsZSBoZXJlOgoKYGBge3J9CgogICMgYSBsaXR0bGUgdW5pdC1mdeKEou+4jyBwbGF5IQoKICBzdHJlbmd0aF9pbl9wc2kgPSBzZXRfdW5pdHMoeCAgICAgPSBjb25jcmV0ZV91bml0cyRDb21wcmVzc2l2ZV9TdHJlbmd0aF8yOGR5LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YWx1ZSA9ICJwc2kiKQoKICBwcmludChjb25jcmV0ZV91bml0cyRDb21wcmVzc2l2ZV9TdHJlbmd0aF8yOGR5WzFdKQogIHByaW50KHN0cmVuZ3RoX2luX3BzaVsxXSkKICAKICAjIE9rIG5vdyBJJ20gYmVpbmcgc2lsbHkgYnV0IHNvIHdlcmUgdGhlIHBhY2thZ2UgZGV2ZWxvcGVycy4gIAogICMgQmxhbWUgdGhlbS4gIAogICMgKE9uY2UgYWdhaW4sIEkgY2FuJ3QgZG8gb2ZmaWNpYWwgRkZGIHVuaXRzKQoKICBjZW1lbnRfaW5fc2x1Z19wZXJfY3UzID0gc2V0X3VuaXRzKHggICAgID0gY29uY3JldGVfdW5pdHMkQ2VtZW50LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFsdWUgPSAic2x1Z3MvZnVybG9uZ3NeMyIpCiAgCiAgcHJpbnQoY29uY3JldGVfdW5pdHMkQ2VtZW50WzFdKQogIHByaW50KGNlbWVudF9pbl9zbHVnX3Blcl9jdTNbMV0pCiAgCiAgCiAgIyBjbGVhbmluZy11cCBvdXIgaG9yc2VwbGF5Li4KICAKICByZW1vdmUoc3RyZW5ndGhfaW5fcHNpKQogIHJlbW92ZShjZW1lbnRfaW5fc2x1Z19wZXJfY3UzKQogIAogIHJlbW92ZShjb25jcmV0ZV91bml0cykKICAKYGBgCkNhdmVhdCEgIEFzIHVzZWZ1bCBhcyB0aGlzIGNhbiBiZSwga25vdyB0aGlzOiAgTm90IGFsbCBSIGZ1bmN0aW9ucyBwbGF5IG5pY2Ugd2l0aCB1bml0cyBvciBvdGhlciAiYXR0cmlidXRlcyIgaW4gZGF0YSBmcmFtZXMgIFNvbWUgb2YgdGhlIHBsb3R0aW5nIHJvdXRpbmVzIGFuZCBsaW5lYXIgcmVncmVzc2lvbiByb3V0aW5lcyBiZWxvdyB3aWxsIHdvcmsgd2l0aCB0aGlzLgoKSWYgeW91IG5lZWQgeW91ciB1bml0cyBhbmQgd2FudCB0byBtaW5pbWl6ZSAibWVzc3kiIGNvZGUgaW4gUiB3aGVuIGl0IGNvbmZsaWN0cyBhbnkgZ2l2ZW4gZnVuY3Rpb24uICBZb3UgY2FuIGxhdGVyIHN0cmlwIG91dCB1bml0cyBieSB1c2luZyB0aGUgW2FzLm51bWVyaWMoKV0oaHR0cHM6Ly93d3cucmRvY3VtZW50YXRpb24ub3JnL3BhY2thZ2VzL2Jhc2UvdmVyc2lvbnMvMy41LjEvdG9waWNzL251bWVyaWMpIGZ1bmN0aW9uCgoKIyA0LiBTb21lIEJhc2ljIFN0YXRpc3RpY3MgYW5kIFRyYWRpdGlvbmFsIFNpbmdsZSBWYXJpYWJsZSBQbG90cwoKTGV0cyBzdGFydCB3aXRoIHNvbWUgYmFzaWMgc3RhdGlzdGljcyBhbmQgcGxvdHRpbmcgb2YgdGhlbS4KCiMjIDQuMS4gVGhlICJjbGFzc2ljIiBzdGF0cwoKTGV0J3MgZ2V0IHRoZSBtb20tYW5kLWFwcGxlLXBpZSBzdGF0cyBmb3IgQ29uY3JldGUKVGhhdCBzZWNvbmQgYXJndW1lbnQgYWxsb3dzIHlvdSB0byBkZWFsIHdpdGggbWlzc2luZyBkYXRhLgoKCgpgYGB7cn0KCiAgIyBzdGF0aXN0aWNzIGZvciBjZW1lbnQKCgogIHByaW50KHN0cl9jKCIgICAgTWVhbiBDZW1lbnQgOiAiLAogICAgICAgICAgICAgIG1lYW4oeCAgICAgPSBjb25jcmV0ZSRDZW1lbnQsICMgdmFyaWFibGUgdG8gY3J1bmNoCiAgICAgICAgICAgICAgICAgICBuYS5ybSA9ICAgICAgICAgICAgVFJVRSkgIyBpZ25vcmUgbXNpc3NpbmcgZGF0YQogICAgICAgICAgICAgICkpCgogIHByaW50KHN0cl9jKCIgICBTdGRldiBDZW1lbnQgOiAiLAogICAgICAgICAgICAgIHNkKHggICAgID0gY29uY3JldGUkQ2VtZW50LCAjIHZhcmlhYmxlIHRvIGNydW5jaAogICAgICAgICAgICAgICAgIG5hLnJtID0gICAgICAgICAgICBUUlVFKSAjIGlnbm9yZSBtc2lzc2luZyBkYXRhCiAgICAgICAgICAgICAgKSkKICAKICBwcmludChzdHJfYygiU2tld25lc3MgQ2VtZW50IDogIiwKICAgICAgICAgICAgICBza2V3bmVzcyh4ICAgICA9IGNvbmNyZXRlJENlbWVudCwgIyB2YXJpYWJsZSB0byBjcnVuY2gKICAgICAgICAgICAgICAgICAgICAgICBuYS5ybSA9ICAgICAgICAgICAgVFJVRSkgIyBpZ25vcmUgbXNpc3NpbmcgZGF0YQogICAgICAgICAgICAgICkpCiAgCiAgcHJpbnQoc3RyX2MoIkt1cnRvc2lzIENlbWVudCA6ICIsCiAgICAgICAgICAgICAga3VydG9zaXMoeCAgICAgPSBjb25jcmV0ZSRDZW1lbnQsICMgdmFyaWFibGUgdG8gY3J1bmNoCiAgICAgICAgICAgICAgICAgICAgICAgbmEucm0gPSAgICAgICAgICAgIFRSVUUpICMgaWdub3JlIG1zaXNzaW5nIGRhdGEKICAgICAgICAgICAgICApKQogICAgIApgYGAKCk9LIHRoaXMgaXMgYSBsaXR0bGUgY2x1bmt5LiAgSXQgd291bGQgYmUgbmljZSBpZiBzb21lb25lIHNvbWV3aGVyZSBtYWRlIGEgc3VwcG9ydCBsaWJyYXJ5IGZvciBSIHRoYXQgd2lsbCBtYWtlIG5pY2UgdGFibGVzIG9mIHN0YXRpc3RpY3MuCgpJbiB0aGlzIGNhc2UgVml2ZSBMYSBGcmFuY2UhIEEgdGVhbSBmcm9tIEZyZW5jaCBSZXNlYXJjaCBJbnN0aXR1dGUgZm9yIEV4cGxvaXRhdGlvbiBvZiB0aGUgU2VhIHRob3VnaHQgdGhlIHNhbWUgcXVlc3Rpb24gYW5kIGFzIGlzIG9mdGVuIHRoZSBjYXNlIGZvciB0aGUgUiBjb21tdW5pdHkgbm90IG9ubHkgZHJhZnRlZCBhIHNldCBvZiB0b29scyB0byBkbyB0aGlzLCAqYW5kKiBtYWRlIGl0IHB1YmxpYy4KCkhlcmUgd2Ugd2FyZSB1c2luZyB0aGVpciBbc3RhdC5kZXNjXShodHRwczovL3d3dy5yZG9jdW1lbnRhdGlvbi5vcmcvcGFja2FnZXMvcGFzdGVjcy92ZXJzaW9ucy8xLjMuMjEvdG9waWNzL3N0YXQuZGVzYykgZnVuY3Rpb24uCgpUaGlzIHdpbGwgaG9wZWZ1bGx5IGdpdmUgcGVvcGxlIHdhbnRpbmcgdG8gbWFrZSBiYXNpYyB0YWJsZXMgIm1heGltdW0gc2F0aXNmYWN0aW9uIHdpdGggbWluaW1hbCBlZmZvcnQuIgoKYGBge3J9CgogICMgUGxvdCBhIHN0YXRpc3RpY3MgdGFibGUgLS0gYWxsIHRoZSBjbGFzc2ljcyBuaWNlIGFuZCBoYW5keSBhbmQgcHJldHR5LgoKICBvcHRpb25zKGRpZ2l0cz0yKSAjIHRoaXMgc2ltcGx5IHNldCB0aGUgZGVjaW1hbCBjb3VudCBpbiB0aGUgdGFibGUgdG8gYmUgY3JlYXRlZCBiZWxvdyAgCiAgICAgICAgICAgICAgICAgICAgIyB0aGlzIHBhcnRpY3VsYXIgZnVuY3Rpb24gY3JlYXRlcyB0aGUgdGFibGUgaW4gc2NpZW50aWZpYyBub3RhdGlvbgogIAogIGNvbmNyZXRlX3N0YXRpc3RpY3MgPSBzdGF0LmRlc2MoeCAgICA9IGNvbmNyZXRlLCAgIyBkYXRhIGZyYW1lCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBiYXNpYyA9ICAgIFRSVUUsICAjIGluY2x1ZGVzIGNvdW50cyBhbmQgZXh0cmVtZXMgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZXNjID0gICAgIFRSVUUsICAjIGluY2x1ZGUgY2xhc3NpYyBzdGF0cyAobWVhbiBldGMpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBub3JtID0gICAgIFRSVUUsICAjIGluY2x1ZGUgbm9ybWFsIGRpc3Qgc3RhdHMgKHNrZXduZXNzIGV0YykKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHAgICAgPSAgICAgMC45NSkgICMgdXNlIDk1JSBjb25maWRlbmNlIGxpbWl0cwoKCiAgcHJpbnQoY29uY3JldGVfc3RhdGlzdGljcykKCmBgYAoKCgojIyA0LjIuIFJlb3JnYW5pemluZyBZb3VyIERhdGEgdG8gSGFuZGxlIE11bHRpcGxlIFZhcmlhYmxlcyBhdCBPbmNlCgpUbyBsZXZlcmFnZSBzb21lIG9mIFIncyBtb3JlIG5pZnR5IGZlYXR1cmVzIHdlIHdpbGwgbmVlZCB0byByZW9yZ2FuaXplIG91ciBkYXRhIGZyb20gYSAic3ByZWFkc2hlZXQgc3R5bGUiIGZvcm1hdCB0byB3aGF0IHNvbWUgcGVvcGxlIGhhdmUgY2FsbGVkIGEgImxvbmcgZm9ybSIgdGFibGUgc28gdGhhdCB0aGUgY29sdW1uIGhlYWRlcnMgb2Ygb3VyIGNvbmNyZXRlIHRyYWl0cyBiZWNvbWUgYSBzaW5nbGUgY29sdW1uIHdpdGggdGhlIHZhbHVlcyBpbiB0aGUgY29sdW1ucyBwbGFjZWQgYWxsIGludG8gYSBzaW5nbGUgY29sdW1uIHNpbWlsYXIgdG8gdGhlIGdyYXBoaWMgYmVsb3cuCgoKIVtFeGFtcGxlIG9mIHRoZSBHYXRoZXIgRnVuY3Rpb25dKGh0dHBzOi8vanVsZXMzMi5naXRodWIuaW8vMjAxNi0wNy0xMi1PeGZvcmQvZHBseXJfdGlkeXIvaW1nL3JzdHVkaW8tY2hlYXRzaGVldC1yZXNoYXBpbmctZGF0YS1nYXRoZXIucG5nKQoKVGhpcyBpcyBkb25lIHdpdGggdGhlIGZ1bmN0aW9uIFtnYXRoZXIoKV0oaHR0cHM6Ly93d3cucmRvY3VtZW50YXRpb24ub3JnL3BhY2thZ2VzL3RpZHlyL3ZlcnNpb25zLzAuOC4xL3RvcGljcy9nYXRoZXIpCgoKYGBge3J9CgogICMgR2F0aGVyaW5nIG91ciBjb21wb25lbnRzIGludG8gYSBzaW5nbGUgY29sdW1uLgoKICAjIFdlIGp1c3Qgd2FudCB0aGUgbmFtZXMgb2Ygb3VyIGNvbXBvbmVudHMgaGVyZSBzbyB3ZSBnZXQgZXZlcnl0aGluZyBwYXN0CiAgIyB0aGUgZmlyc3QgY29sdW1uICh3aGljaCBpcyB0aGUgZXhwZXJpbWVudCBuYW1lKQoKICBjb2x1bW5fbmFtZXMgID0gY29sbmFtZXMoY29uY3JldGVbMjpuY29sKGNvbmNyZXRlKV0pICAgCgogIHRibF9kZihjb2x1bW5fbmFtZXMpICMgdGJsX2RmIG1ha2VzIGl0IGxvb2sgcHJldHR5IHdoZW4gcHJpbnRlZAoKICAjIHRoZSBnYXRoZXIgY29tbWFuZCB3aWxsIGdyb3VwIGV2ZXJ5dGhpbmcuIGluIHRoZSBjb2x1bW4gbmFtZSBncm91cCAKCiAgY29uY3JldGVfdGlkeSA9IGdhdGhlcihkYXRhICA9ICAgIGNvbmNyZXRlLCAjIHlvdXIgZGF0YSBmcmFtZQogICAgICAgICAgICAgICAgICAgICAgICAga2V5ICAgPSAiUGFyYW1ldGVyIiwgIyBjb2x1bW4gbmFtZSBmb3IgeW91ciBmb3JtZXIgY29sdW1ucwogICAgICAgICAgICAgICAgICAgICAgICAgdmFsdWUgPSAgICAgIlZhbHVlIiwgIyBjb2x1bW4gbmFtZSBmb3IgeW91ciBkYXRhCiAgICAgICAgICAgICAgICAgICAgICAgICBjb2x1bW5fbmFtZXMgICAgICAgKSAjIHRoZSBsaXN0IGZvciB0aGUgY29sdW1ucyB0byAiZ2F0aGVyIgoKICAKCiAgIyB0aGlzIHdpbGwgbGV0IHVzIHNvcnQgZnV0dXJlIHBsb3RzIGluIHRoZSBzYW1lIG9yZGVyIGFzIG91ciBwbG90cy4gIAogIAogIGNvbmNyZXRlX3RpZHkkUGFyYW1ldGVyID0gZmFjdG9yKHggICAgICA9IGNvbmNyZXRlX3RpZHkkUGFyYW1ldGVyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxldmVscyA9IGNvbHVtbl9uYW1lcykKICAKICAjIHdlIGNhbiBhbHNvIHNwbGl0IHRoaW5ncyBiZXR3ZWVuIG91ciBkZXBlbmRhbnQgdmFyaWFibGVzIGFuZCBpbmRlcGVuZGFudCB2YXJpYWJsZXMuCiAgCiAgCiAgY29uY3JldGVfaW5kZXBlbmRlbnQgPSBzdWJzZXQoeCAgICAgID0gY29uY3JldGVfdGlkeSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdWJzZXQgPSAoUGFyYW1ldGVyICE9ICJTbHVtcCIpICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAoUGFyYW1ldGVyICE9ICJGbG93IikgICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAoUGFyYW1ldGVyICE9ICJDb21wcmVzc2l2ZV9TdHJlbmd0aF8yOGR5IikKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApIAogICAgCiAgICAKICBjb25jcmV0ZV9kZXBlbmRlbnQgPSBzdWJzZXQoeCAgICAgID0gY29uY3JldGVfdGlkeSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3Vic2V0ID0gKFBhcmFtZXRlciA9PSAiU2x1bXAiKSB8CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIChQYXJhbWV0ZXIgPT0gIkZsb3ciKSAgfAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAoUGFyYW1ldGVyID09ICJDb21wcmVzc2l2ZV9TdHJlbmd0aF8yOGR5IikKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQoKIAogICAgICAgICAgICAgICAgICAgICAgIAoKICBwcmludChjb25jcmV0ZV90aWR5KQogIHByaW50KGNvbmNyZXRlX2luZGVwZW5kZW50KQogIHByaW50KGNvbmNyZXRlX2RlcGVuZGVudCkKICAKICAKYGBgCgoKCgoKIyA1LiBQbG90dGluZyBHcmFwaGljcyB1c2luZyBUaWR5dmVyc2UgUmVzb3VyY2VzCgpSIGhhcyBhIGZldyB3YXlzIHRvIGRvIHRoZSBiYXNpYyBoaXN0b2dyYW1zLCBCb3hwbG90cyBhbmQgb3RoZXIgZGlzdHJpYnV0aW9uIHBsb3RzLgoKVGhlcmUgYXJlIGEgbnVtYmVyIG9mIHNwaWZmeSB3YXlzIHRvIHBsb3QgdGhlc2Ugc3RhdGlzdGljYWwgcGxvdHMgaW4gUi4gV2UncmUganVzdCB1c2luZyBvbmUgaGVyZS4uLgoKIyMgNS4xLiAgU0xPT09PV1dXV0xMTExMWVlZIE1ha2luZyBhIFNpbXBsZSBQbG90IChIaXN0b2dyYW0gRWRpdGlvbikKCk5vdyBJJ20gZ29pbmcgdG8gZG8gdGhpcyBvbmUgdGlueSBzdGVwIGF0IGEgdGltZSB1bnRpbCB3ZSBnZXQgdG8gYSB2aWFibGUgcHJvZHVjdC4gIChUaGlzIGlzIGhvdyBJIHdvcmsgdGhyb3VnaCBjcnlwdGljIHByb2NlZHVyZXMgc28gSSBjYW4gc2VlIHdoYXQgZWFjaCBsaXR0bGUgYWRkaXRpb25hbCBteXN0ZXJ5IHRoaW5naWUgZG9lcy4pCgpHcmFwaGluZyBpcyBpbnZva2VkIGJ5IHRoZSBbZ2dwbG90Ml0oaHR0cHM6Ly9nZ3Bsb3QyLnRpZHl2ZXJzZS5vcmcpIGNvbW1hbmQuLiB3aGljaCBoYXMgYSBoZWx1dmFsb3QgdW5kZXIgaXRzIGhvb2QhICBGb3IgbWUgYWxsIHRoYXQgZGV0YWlsIHdhcyB3aGF0IGhhZCBtZSBhIGxpdHRsZSBzaHkgdG8gYWRvcHQgdGhpcyB3YXkgb2YgcHJpbnRpbmcgZGF0YS4KClRpZHl2ZXJzZSB1c2VzIHdoYXQgaXMgc29tZXRpbWVzIGNhbGxlZCB0aGUgWyJncmFtbWFyIG9mIGdyYXBoaWNzIl0oaHR0cHM6Ly9yYW1uYXRodi5naXRodWIuaW8vcHljb24yMDE0LXIvdmlzdWFsaXplL2dncGxvdDIuaHRtbCkgbWV0aG9kLi4uIHRvIG1ha2UgYSBsb25nIHN0b3J5IGxvbmdlciwgdGhlIEdvRyBwcmVzZW50cyBzZXBhcmF0ZSBjb21tYW5kcyB0byBkbyBzZXBhcmF0ZSB0aGluZ3MgcmF0aGVyIGJ1bmRsZSBzdHVmZiBpbiBhIHNpbmdsZSBncmFwaGluZyBmdW5jdGlvbi4gIFNvbWV0aW1lcyBpdCBtYWtlcyBhIGxvdCBvZiBzZW5zZS4uLiBvdGhlciB0aW1lcyBpdCBtYXkgYmUgY29uZnVzaW9uLiAgKEhlbmNlIG1lIGRlbW9uc3RyYXRpbmcgbWFraW5nIGEgZ3JhcGggdGhpcyBvbmUgdGlueSBzdGVwIGF0IGEgdGltZSEKCgpGaXJzdCB0aGluZyB3ZSBhcmUgZ29pbmcgdG8gZG8gaXMgb3BlbiBhIHBsb3R0aW5nIHNwYWNlIHdpdGggdGhlIGNvbW1hbmQgW2dncGxvdCgpXShodHRwczovL2dncGxvdDIudGlkeXZlcnNlLm9yZy9yZWZlcmVuY2UvZ2dwbG90Lmh0bWwpCgpgYGB7cn0KCiMgaW52b2tlIHRoZSBnZ3Bsb3QgcGxvdHRpbmcgZW52aXJvbm1uZW50LgoKZ2dwbG90KCkgCgpgYGAKCldvdy4gIFdlIGhhdmUgYS4uLiBiaWcgc3F1YXJlIG9mLi4uIGdyZXkuICBBbGwgaXQncyBkb2luZyBpcyBzZXR0aW5nIHVwIG91ciBwbG90IGVudmlyb25tZW50Li4uIHNvIGxldCdzIGRvIHNvbWUgbW9yZS4uLgoKSWYgd2Ugd2FudCB0byBkbyBhIGhpc3RvZ3JhbSB3ZSBhcmUgZ29pbmcgdG8gaGF2ZSB0byB0ZWxsIGl0IHdoYXQgd2Ugd2FudCB0byBwcmludCBhbmQgd2hlcmUgdG8gZ2V0IHRoZSBzdHVmZgoKV2hlbiB3ZSBhZGQgdGhpbmdzIHRvIGEgcGxvdCBjb21tYW5kIGluIFRpZHl2ZXJzZSB3ZSAiYWRkIiB0byB0aGUgc3RlcHMgaW5jcmVtZW50YWxseS4KClRoaXMgaW52b2x2ZXMgYSAibWFwcGluZyIgZnVuY3Rpb24gY2FsbGVkICJbYWVzXShodHRwczovL2dncGxvdDIudGlkeXZlcnNlLm9yZy9yZWZlcmVuY2UvYWVzLmh0bWwpIiAoc2hvcnQgZm9yIGFlc3RoZXRpY3MpCgpoZXJlLCB3ZSBhcmUgd29ya2luZyB3aXRoIHRoZSBkYXRhIGZyYW1lICJjb25jcmV0ZSIgYW5kIGFyZSB3b3JraW5nIG9uIHRoZSB2YXJpYWJsZSBDZW1lbnQgd2hpY2ggd2UgYXJlIHRvc3Npbmcgb250byB0aGUgeCBheGlzIGJlY2F1c2UgdGhhdCdzIHdoZXJlIHRoZSBiaW5zIG9mIGNlbWVudCBnbyEKCmBgYHtyfQoKZ2dwbG90KGRhdGEgPSBjb25jcmV0ZSkgKyAgICMgRURJVDogIGludm9rZSBncmFwaGljcyBlbnZpcm9ubWVudCB1c2luZyBhIGdpdmVuIGRhdGFmcmFtZQogIAogIGFlcyh4ICAgID0gQ2VtZW50KSAgICAgICAgIyBORVc6IHNlbGVjdCB2YXJpYWJsZSB0byBwcmludC4uLiBZb3UgY2FuIGdldCByZWFsbHkgZmFuY3kgaGVyZSBsYXRlcgoKYGBgCgpPSyBub3cgd2UgaGF2ZSBzb21ldGhpbmcgdGhhdCBsb29rcyBsaWtlIHdlIG1heSBoYXZlIHRoZSBtYWtpbmcgb2YgdGhlIGdyYXBoLiAgSWYgeW91IGRvbid0IGxpa2UgZ3JleSBvdXRsaW5lcyBhbmQgd2hpdGUgZ3JpZHMsIG5vIHdvcnJpZXMsIHdlIGNhbiBjaGFuZ2UgdGhhdCBzaG9ydGx5LgoKT0suLiB3ZSBhcmUgbm93IHJlYWR5IHRvIG1ha2UgYSBoaXN0b2dyYW0uLi4gCgpIZXJlIHdlIHdpbGwgdXNlIG9uZSBvZiB0aGUgZ2dsb3QyJ3MgImdlb21fKiIgKGRyYXcgc3R1ZmYpIHJlc291cmNlcy4gIFRoZSBkZWZhdWx0IHNob3VsZCB3b3JrIGZvciB1cyBoZXJlLgoKYGBge3J9CgpnZ3Bsb3QoZGF0YSA9IGNvbmNyZXRlKSArICAgIyBpbnZva2UgZ3JhcGhpY3MgZW52aXJvbm1lbnQgdXNpbmcgYSBnaXZlbiBkYXRhZnJhbWUKICAKICBhZXMoeCA9IENlbWVudCkgICArICAgICAgICMgc2VsZWN0IHZhcmlhYmxlIHRvIHByaW50Li4uIFlvdSBjYW4gZ2V0IHJlYWxseSBmYW5jeSBoZXJlIGxhdGVyCgogIGdlb21faGlzdG9ncmFtKCkgICAgICAgICAgIyBORVc6IGluc2VydCBoaXN0b2dyYW0KCmBgYAoKKHlvdSBtYXkgaGF2ZSBnb3R0ZW4gYSB3YXJuaW5nIGFib3V0IHVzaW5nIHRoZSBiaW49WCwgeW91IGNhbiBhZGp1c3QgaXQuKQoKTm93IHF1aWNrbHkgYmVmb3JlIG1vdmluZyBvbi4uLiBJIGFtIG5vdCBrZWVuIG9uICB0aGUgZ3JleSBiYWNrZ3JvdW5kIHdpdGggd2hpdGUgbGluZXMuICAKClRoZXJlIGFyZSBhIG51bWJlciBvZiBvdXQtb2YtdGhlLWJveCBbInRoZW1lcyJdKGh0dHBzOi8vZ2dwbG90Mi50aWR5dmVyc2Uub3JnL3JlZmVyZW5jZS9nZ3RoZW1lLmh0bWwpIGZvciBnZ3Bsb3QyLiAgCgpJJ20gcGFydGlhbCB0byB0aGVtZV9idygpIGFuZCB0aGVtZV9saWdodCgpIGJ1dCB0cnkgdGhlIG9uZXMgdGhhdCB5b3UgcHJlZmVyIG9yIHN0aWNrIHdpdGggdGhlIGRlZmF1bHQsIHRoZW1lX2dyYXkoKS4gIAoKVGhlc2UgcGxvdHMgc2hvd24gaGVyZSBhcmUgbWluZS4gIFlvdSBzaG91bGQgZmlkZ2V0IGFib3V0IHNvIHRoZXkgYXJlICp5b3VycyogYW5kIHNvIHlvdSBjYW4gYWRhcHQgdG8gdGhpcyBuZXcgd2F5IG9mIHdvcmtpbmcgd2l0aCBkYXRhLgoKCgpgYGB7cn0KCmdncGxvdChkYXRhID0gY29uY3JldGUpICsgIyBpbnZva2UgZ3JhcGhpY3MgZW52aXJvbm1lbnQgdXNpbmcgYSBnaXZlbiBkYXRhZnJhbWUKICAKICB0aGVtZV9idygpICsgICAgICAgICAgICAjIE5FVzogY2hhbmdpbmcgdGhlIHBsb3R0aW5nIHRoZW1lCiAgCiAgYWVzKHggPSBDZW1lbnQpICsgICAgICAgIyBzZWxlY3QgdmFyaWFibGUgdG8gcHJpbnQuLi4gWW91IGNhbiBnZXQgcmVhbGx5IGZhbmN5IGhlcmUgbGF0ZXIKCiAgZ2VvbV9oaXN0b2dyYW0oKSAgICAgICAgIyBpbnNlcnQgaGlzdG9ncmFtIChpbmNsdWRpbmcgY29udHJvbGxpbmcgbnVtYmVyIG9mIGJpbnMpCgpgYGAKCk15IE9DRCBoYXRlcyBheGVzIHdoZXJlIHRoZSBsYWJlbHMgZG9uJ3QgZW52ZWxvcCBhbGwgb2YgdGhlIGRhdGEuLi4gCgpXZSBjYW4gZml4IHRoYXQgd2l0aCBbeGxpbSgpIG9yIHlsaW0oKV0oaHR0cHM6Ly9nZ3Bsb3QyLnRpZHl2ZXJzZS5vcmcvcmVmZXJlbmNlL2xpbXMuaHRtbCkKCmBgYHtyfQoKZ2dwbG90KGRhdGEgPSBjb25jcmV0ZSkgKyAgICAgIyBpbnZva2UgZ3JhcGhpY3MgZW52aXJvbm1lbnQgdXNpbmcgYSBnaXZlbiBkYXRhZnJhbWUKICAKICB0aGVtZV9idygpICsgICAgICAgICAgICAgICAgIyBjaGFuZ2luZyB0aGUgcGxvdHRpbmcgdGhlbWUKICAKICBhZXMoeCA9IENlbWVudCkgKyAgICAgICAgICAgIyBzZWxlY3QgdmFyaWFibGUgdG8gcHJpbnQuLi4gWW91IGNhbiBnZXQgcmVhbGx5IGZhbmN5IGhlcmUgbGF0ZXIKICAKICB4bGltKCAxMDAsIDQwMCApICsgICAgICAgICAgIyBORVc6IGFkZGluZyB4LWF4aXMgbGltaXRzCgogIGdlb21faGlzdG9ncmFtKCkgICAgICAgICAgICAjIGluc2VydCBoaXN0b2dyYW0KCmBgYAoKSG93IGFib3V0IGNoYW5naW5nIHRoZSBjb2xvciBvZiB0aGUgZmlsbCBpbiB0aGUgYmFycy4uLgoKW1lvdSByZWFsbHkgZG9uJ3Qgd2FudCB0byBrbm93IGFib3V0IGFsbCB0aGUgY29sb3JzIHlvdSBjYW4gdXNlLl0oaHR0cHM6Ly93d3cubmNlYXMudWNzYi5lZHUvfmZyYXppZXIvUlNwYXRpYWxHdWlkZXMvY29sb3JQYWxldHRlQ2hlYXRzaGVldC5wZGYpCgpgYGB7cn0KCgpnZ3Bsb3QoZGF0YSA9IGNvbmNyZXRlKSArICAgICAjIGludm9rZSBncmFwaGljcyBlbnZpcm9ubWVudCB1c2luZyBhIGdpdmVuIGRhdGFmcmFtZQogIAogIHRoZW1lX2J3KCkgKyAgICAgICAgICAgICAgICAjIGNoYW5naW5nIHRoZSBwbG90dGluZyB0aGVtZQogIAogIGFlcyh4ID0gQ2VtZW50KSArICAgICAgICAgICAjIHNlbGVjdCB2YXJpYWJsZSB0byBwcmludC4uLiBZb3UgY2FuIGdldCByZWFsbHkgZmFuY3kgaGVyZSBsYXRlcgogIAogIHhsaW0oIDEwMCwgNDAwICkgKyAgICAgICAgICAjIE5FVzogYWRkaW5nIHgtYXhpcyBsaW1pdHMKCiAgZ2VvbV9oaXN0b2dyYW0oZmlsbD0iZ3JheSIpICMgRURJVDogaW5zZXJ0IGhpc3RvZ3JhbSAod2l0aCBhIHNpbmdsZSBjaG9zZW4gY29sb3IpCgpgYGAKCldhbnQgdG8gY3VzdG9taXplIHRoZSBsYWJlbHMgYW5kIHRpdGxlcyBzbyB3ZSBjYW4gaGF2ZSB1bml0cz8KCllvdSBjYW4gYWRkIGN1c3RvbSBsYWJlbHMgYW5kIHRpdGxlcyEgIChodHRwczovL3d3dy5uY2Vhcy51Y3NiLmVkdS9+ZnJhemllci9SU3BhdGlhbEd1aWRlcy9jb2xvclBhbGV0dGVDaGVhdHNoZWV0LnBkZikKCkZvciB0aGUgc3VwZXJzY3JpcHRpbmcgaW4gdGhlIHgtYXhpcyBsYWJlbCwgSSBhbSB1c2luZyB0aGUgW2V4cHJlc3Npb24oKV0oaHR0cDovL3Zpcy5zdXBzdGF0LmNvbS8yMDEzLzA0L21hdGhlbWF0aWNhbC1hbm5vdGF0aW9uLWluLXIvKSB0b29sIGluIFIuCgpgYGB7cn0KCgpnZ3Bsb3QoZGF0YSA9IGNvbmNyZXRlKSArICAgICAjIGludm9rZSBncmFwaGljcyBlbnZpcm9ubWVudCB1c2luZyBhIGdpdmVuIGRhdGFmcmFtZQogIAogIHRoZW1lX2J3KCkgKyAgICAgICAgICAgICAgICAjIGNoYW5naW5nIHRoZSBwbG90dGluZyB0aGVtZQogIAogIGFlcyh4ID0gQ2VtZW50KSArICAgICAgICAgICAjIHNlbGVjdCB2YXJpYWJsZSB0byBwcmludC4uLiBZb3UgY2FuIGdldCByZWFsbHkgZmFuY3kgaGVyZSBsYXRlcgogIAogIHhsaW0oIDEwMCwgNDAwICkgKyAgICAgICAgICAjIGFkZGluZyB4LWF4aXMgbGltaXRzCgogIGdndGl0bGUoIlllaCBTdXBlcnBsYXN0aWNpemVyIFRlc3RzIikgKyAgICAgICAgICAjIE5FVyA6IEN1c3RvbSBUaXRsZQogIAogIHhsYWIoZXhwcmVzc2lvbignQ2VtZW50IEFtb3VudCAoa2cgbSdeLTMqIikiKSkgKyAjIE5FVyA6IEN1c3RvbSBBeGlzIExhYmVsCgogIGdlb21faGlzdG9ncmFtKGZpbGw9ImdyYXkiKSAjIGluc2VydCBoaXN0b2dyYW0gKHdpdGggYSBzaW5nbGUgY2hvc2VuIGNvbG9yKQoKYGBgCkFuZCBJIGNvdWxkIGtlZXAgdHdlYWtpbmcgdGhpcyBncmFwaCBhbGwgZGF5LCBidXQgZ29vZCBlbm91Z2ggaXMgZ29vZCBlbm91Z2ggc28gdGhpcyBpcyBhIGdvb2QgcGxhY2UgdG8gc3RvcC4uLiAKCldlIGFsc28gY2FuIHBsb3QgYSBmZXcgb3RoZXIgZmllbGRzIHdpdGggc29tZSB0cmlhbCBhbmQgZXJyb3IuLiAKCmBgYHtyfQoKIyBIaXN0b2dyYW0gb2YgV2F0ZXIKCmdncGxvdChkYXRhID0gY29uY3JldGUpICsgICAgICMgaW52b2tlIGdyYXBoaWNzIGVudmlyb25tZW50IHVzaW5nIGEgZ2l2ZW4gZGF0YWZyYW1lCiAgCiAgdGhlbWVfYncoKSArICAgICAgICAgICAgICAgICMgY2hhbmdpbmcgdGhlIHBsb3R0aW5nIHRoZW1lCiAgCiAgYWVzKHggPSBXYXRlcikgKyAgICAgICAgICAgIyBzZWxlY3QgdmFyaWFibGUgdG8gcHJpbnQuLi4gWW91IGNhbiBnZXQgcmVhbGx5IGZhbmN5IGhlcmUgbGF0ZXIKICAKICB4bGltKCAxNTAsIDI1MCApICsgICAgICAgICAgIyBhZGRpbmcgeC1heGlzIGxpbWl0cwoKICBnZ3RpdGxlKCJZZWggU3VwZXJwbGFzdGljaXplciBUZXN0cyIpICsgI0N1c3RvbSBUaXRsZQogIAogIHhsYWIoZXhwcmVzc2lvbignV2F0ZXIgQW1vdW50IChrZyBtJ14tMyoiKSIpKSArICMgTkVXIDogQ3VzdG9tIEF4aXMgTGFiZWwgbm90ZSB1c2Ugb2Ygc3VwZXJzY3JpcHRzIGZyb20gYWJvdmUKCiAgZ2VvbV9oaXN0b2dyYW0oZmlsbD0iYmx1ZSIpICMgaW5zZXJ0IGhpc3RvZ3JhbSAod2l0aCBhIHNpbmdsZSBjaG9zZW4gY29sb3IpCgpgYGAKCmBgYHtyfQoKIyBIaXN0b2dyYW0gb2YgU3RyZW5ndGgKCmdncGxvdChkYXRhID0gY29uY3JldGUpICsgICAgICMgaW52b2tlIGdyYXBoaWNzIGVudmlyb25tZW50IHVzaW5nIGEgZ2l2ZW4gZGF0YWZyYW1lCiAgCiAgdGhlbWVfYncoKSArICAgICAgICAgICAgICAgICMgY2hhbmdpbmcgdGhlIHBsb3R0aW5nIHRoZW1lCiAgCiAgYWVzKHggPSBDb21wcmVzc2l2ZV9TdHJlbmd0aF8yOGR5KSArICMgc2VsZWN0IHZhcmlhYmxlIHRvIHByaW50Li4uIFlvdSBjYW4gZ2V0IHJlYWxseSBmYW5jeSBoZXJlIGxhdGVyCiAgCiAgeGxpbSggMTAsIDYwICkgKyAgICAgICAgICAjIGFkZGluZyB4LWF4aXMgbGltaXRzCgogIGdndGl0bGUoIlllaCBTdXBlcnBsYXN0aWNpemVyIFRlc3RzIikgKyAjQ3VzdG9tIFRpdGxlCiAgCiAgeGxhYigiMjgtZHkgQ29tcHJlc3NpdmUgU3RyZW5ndGggKE1QYSkiKSArICMgTkVXIDogQ3VzdG9tIEF4aXMgTGFiZWwKCiAgZ2VvbV9oaXN0b2dyYW0oZmlsbD0icmVkIikgIyBpbnNlcnQgaGlzdG9ncmFtICh3aXRoIGEgc2luZ2xlIGNob3NlbiBjb2xvcikKCmBgYAoKKEFuZCBmcm9tIG91ciBJbnRybyB0byBTdGF0cyBMZWN0dXJlLi4uKQoKCmBgYHtyfQoKIyBIaXN0b2dyYW0gb2YgU3RyZW5ndGgKCmdncGxvdChkYXRhID0gY29uY3JldGUpICsgICAgICMgaW52b2tlIGdyYXBoaWNzIGVudmlyb25tZW50IHVzaW5nIGEgZ2l2ZW4gZGF0YWZyYW1lCiAgCiAgdGhlbWVfYncoKSArICAgICAgICAgICAgICAgICMgY2hhbmdpbmcgdGhlIHBsb3R0aW5nIHRoZW1lCiAgCiAgYWVzKHggPSBTbHVtcCkgKyAjIHNlbGVjdCB2YXJpYWJsZSB0byBwcmludC4uLiBZb3UgY2FuIGdldCByZWFsbHkgZmFuY3kgaGVyZSBsYXRlcgogIAogIHhsaW0oIDAsIDMwICkgKyAgICAgICAgICAjIGFkZGluZyB4LWF4aXMgbGltaXRzCgogIGdndGl0bGUoIlllaCBTdXBlcnBsYXN0aWNpemVyIFRlc3RzIikgKyAjQ3VzdG9tIFRpdGxlCiAgCiAgeGxhYigiU2x1bXAgKGNtKSIpICsgIyBORVcgOiBDdXN0b20gQXhpcyBMYWJlbAoKICBnZW9tX2hpc3RvZ3JhbShmaWxsPSJkYXJrZ3JlZW4iKSAjIGluc2VydCBoaXN0b2dyYW0gKHdpdGggYSBzaW5nbGUgY2hvc2VuIGNvbG9yKQoKYGBgCgojIyA1LjIgRGlzdHJpYnV0aW9uIFBsb3QgW25vdCBzbyBnb29kIGFuXSBFeGFtcGxlCgpUaGVyZSBhcmUgc29tZSBvdGhlciBwbG90cyB0aGF0IHdlIGNhbiB1c2UgdG8gZGVzY3JpYmUgb3VyIGRhdGEuCgpIZXJlIHRvIHBsYXkgd2l0aCB0aGVtIHdlIHdpbGwgdGFrZSBhIHF1aWNrIHN0ZXAgYmFjayBhbmQgYWRkcmVzcyB0aGF0ICJ0aWR5IidlZCAoc2hvdWxkIHRoYXQgc2F5ICJ0aWRpZWQiPykgZGF0YWZyYW1lICJjb25jcmV0ZV90aWR5IgoKV2UgY2FuIG5vdyB1c2UgYWxsIHRoZSBwYXJhbWV0ZXJzIGluIHRoZSAidGlkeSIgKGxvbmcpIGRhdGEgZnJhbWUgdG8gcHJpbnQgYnkgc3BlY2lmaWMgdHJhaXRzLgoKCmBgYHtyfQoKZ2dwbG90KGRhdGEgPSBjb25jcmV0ZV90aWR5KSArICAgICAgICAgICAgIyBpbnZva2UgZ3JhcGhpY3MgZW52aXJvbm1lbnQgdXNpbmcgYSBnaXZlbiBkYXRhZnJhbWUKICAKICB0aGVtZV9idygpICsgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBjaGFuZ2luZyB0aGUgcGxvdHRpbmcgdGhlbWUKICAKICBhZXMoeCAgICAgID0gVmFsdWUsICAgICAgICAgICAgICAgICAgICAgIyBtYXAgeC1heGlzIHZhbHVlCiAgICAgIGNvbG9yICA9IFBhcmFtZXRlcikgKyAgICAgICAgICAgICAgICMgbWFwIGNvbG9ycyBmb3IgZGlmZmVyZW50IHF1YWxpdHkKICAKICBnZ3RpdGxlKCJZZWggU3VwZXJwbGFzdGljaXplciBUZXN0cyIpICsgIyBDdXN0b20gVGl0bGUKICAKICB4bGFiKCJWYWx1ZSIpICsgICAgICAgICAgICAgICAgICAgICAgICAgIyAgQ3VzdG9tIEF4aXMgTGFiZWwKCiAgZ2VvbV9kZW5zaXR5KCkgICAgICAgICAgICAgICAgICAgICAgICAgICMgaW5zZXJ0IGNyZXRlIGEgcmVsYXRpdmUgZGVuc2l0eSBwbG90IAoKYGBgCgpJbiB0aGUgcGFzdCwgSSd2ZSBnb3R0ZW4gZ29vZCByZXN1bHRzIHdpdGggdGhpcyBidXQgaW4gdGhpcyBjYXNlLCBJIHRoaW5rIGl0J3MgdG9vIG1lc3N5IGluIHBhcnQgZHVlIHRvIHRoZSBkaXNwYXJpdHkgaW4gdGhlIGR5bmFtaWMgcmFuZ2Ugb2Ygb3VyIHBhcmFtZXRlcnMuIAoKIyMgNS4zLiBCb3gtV2hpc2tlciBQbG90IEV4YW1wbGUKCkhvdyBhYm91dCBsZXZlcmFnaW5nIGEgYm94IHdoaXNrZXI/ICAoSSdtIHVzaW5nIG9ubHkgdGhlIGluZGVwZW5kZW50IHZhcmlhYmxlcyB0aGlzIHRpbWUuKQoKCmBgYHtyfQoKZ2dwbG90KGRhdGEgPSBjb25jcmV0ZV9pbmRlcGVuZGVudCkgKyAgICAgICMgRURJVCBDaGFuZ2luZyBkYXRhZnJhbWUKICAKICB0aGVtZV9idyggKSArICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgY2hhbmdpbmcgdGhlIHBsb3R0aW5nIHRoZW1lCiAgCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCkpICsgICAjIGFkZGluZyBhbiBleHRyYSB0cmFpdCB0byB0aGUgeC1heGlzCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIHRvIG5vdCBwcmludCBsYWJlbHMgb24gdGhlIHgtYXhpcyAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgKHRoZSBsYWJlbHMgb3ZlcmxhcCBhbmQgZG9lc24ndCBsb29rCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIHByZXR0eS4uLikKICAKICBhZXMoeSAgICAgID0gVmFsdWUsICAgICAgICAgICAgICAgICAgICAgIyBtYXAgeS1heGlzIHZhbHVlCiAgICAgIHggICAgICA9IFBhcmFtZXRlciwgICAgICAgICAgICAgICAgICMgbWFwIHgtYXhpcyB2YWx1ZQogICAgICBjb2xvciAgPSBQYXJhbWV0ZXIpICsgICAgICAgICAgICAgICAjIG1hcCBjb2xvcnMgZm9yIGRpZmZlcmVudCBxdWFsaXR5CiAgCiAgZ2d0aXRsZShsYWJlbCAgICA9ICJZZWggU3VwZXJwbGFzdGljaXplciBUZXN0cyIsCiAgICAgICAgICBzdWJ0aXRsZSA9ICJDb25jcmV0ZSBUZXN0IENvbXBvbmVudHMiKSArICMgQ3VzdG9tIFRpdGxlCiAgCiAgeWxhYihleHByZXNzaW9uKCdBbW91bnQgKGtnIG0nXi0zKiIpIikpICsgIyBFRElUIDogQ2hhbmdpbmcgQ3VzdG9tIEF4aXMgTGFiZWwKCiAgZ2VvbV9ib3hwbG90KCkgICAgICAgICAgICAgICAgICAgICAgICAgICMgaW5zZXJ0IGNyZXRlIGEgcmVsYXRpdmUgZGVuc2l0eSBwbG90IAoKYGBgCgpXaGF0IGFib3V0IG91ciBkZXBlbmRhbnQgdmFyaWFibGVzPyAgV2UgY2FuIHN0YXJ0IGJ5IGNoYW5naW5nIHRoZSBkYXRhIGZyYW1lLi4uCgpgYGB7cn0KCmdncGxvdChkYXRhID0gY29uY3JldGVfZGVwZW5kZW50KSArICAgICAgIyBFRElUIENoYW5naW5nIGRhdGFmcmFtZQogIAogIHRoZW1lX2J3KCApICsgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBjaGFuZ2luZyB0aGUgcGxvdHRpbmcgdGhlbWUKICAKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfYmxhbmsoKSkgKyAgICMgYWRkaW5nIGFuIGV4dHJhIHRyYWl0IHRvIHRoZSB4LWF4aXMKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgdG8gbm90IHByaW50IGxhYmVscyBvbiB0aGUgeC1heGlzIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyAodGhlIGxhYmVscyBvdmVybGFwIGFuZCBkb2Vzbid0IGxvb2sKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgcHJldHR5Li4uKQogIAogIGFlcyh5ICAgICAgPSBWYWx1ZSwgICAgICAgICAgICAgICAgICAgICAjIG1hcCB5LWF4aXMgdmFsdWUKICAgICAgeCAgICAgID0gUGFyYW1ldGVyLCAgICAgICAgICAgICAgICAgIyBtYXAgeC1heGlzIHZhbHVlCiAgICAgIGNvbG9yICA9IFBhcmFtZXRlcikgKyAgICAgICAgICAgICAgICMgbWFwIGNvbG9ycyBmb3IgZGlmZmVyZW50IHF1YWxpdHkKICAKICBnZ3RpdGxlKGxhYmVsICAgID0gIlllaCBTdXBlcnBsYXN0aWNpemVyIFRlc3RzIiwKICAgICAgICAgIHN1YnRpdGxlID0gIkNvbmNyZXRlIFRlc3QgUmVzdWx0cyIpICsgIyBDdXN0b20gVGl0bGUKICAKICB5bGFiKCJWYWx1ZXMiKSArCgogIGdlb21fYm94cGxvdCgpICAgICAgICAgICAgICAgICAgICAgICAgICAjIGluc2VydCBjcmV0ZSBhIHJlbGF0aXZlIGRlbnNpdHkgcGxvdCAKCmBgYAoKV2FudCB1bml0cz8gIFRoYXQncyBhIGxpdHRsZSB0b3VnaGVyIGhlcmUgc2luY2UgdGhlIHVuaXRzIGRpZmZlciBieSBwYXJhbWV0ZXIuICBXZSBjYW4gZm9yY2UgdGhlIHZhbHVlcyB0byBpbnRvIG5ldyBuYW1lcyB0aG91Z2guCgpgYGB7cn0KCmdncGxvdChkYXRhID0gY29uY3JldGVfZGVwZW5kZW50KSArICAgICAgIyBFRElUIENoYW5naW5nIGRhdGFmcmFtZQogIAogIHRoZW1lX2J3KCApICsgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBjaGFuZ2luZyB0aGUgcGxvdHRpbmcgdGhlbWUKICAKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfYmxhbmsoKSkgKyAgICMgYWRkaW5nIGFuIGV4dHJhIHRyYWl0IHRvIHRoZSB4LWF4aXMKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgdG8gbm90IHByaW50IGxhYmVscyBvbiB0aGUgeC1heGlzIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyAodGhlIGxhYmVscyBvdmVybGFwIGFuZCBkb2Vzbid0IGxvb2sKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgcHJldHR5Li4uKQogIAogIGFlcyh5ICAgICAgPSBWYWx1ZSwgICAgICAgICAgICAgICAgICAgICAjIG1hcCB5LWF4aXMgdmFsdWUKICAgICAgeCAgICAgID0gUGFyYW1ldGVyLCAgICAgICAgICAgICAgICAgIyBtYXAgeC1heGlzIHZhbHVlCiAgICAgIGNvbG9yICA9IFBhcmFtZXRlcikgKyAgICAgICAgICAgICAgICMgbWFwIGNvbG9ycyBmb3IgZGlmZmVyZW50IHF1YWxpdHkKICAKICBnZ3RpdGxlKGxhYmVsICAgID0gIlllaCBTdXBlcnBsYXN0aWNpemVyIFRlc3RzIiwKICAgICAgICAgIHN1YnRpdGxlID0gIkNvbmNyZXRlIFRlc3QgUmVzdWx0cyIpICsgIyBDdXN0b20gVGl0bGUKICAKICB5bGFiKCJWYWx1ZXMiKSArCgogICMgTkVXOiBJdCBzYXlzIHNjYWxlIGNvbG9yIGJ1dCAiY29sb3IiIGlzIGhvdyB3ZSBhcmUgZGlzdGluZ3Vpc2hpbmcKICAjICAgICAgb3V0IGJveHBsb3RzIChhcyBzZWVuIGluIHRoZSBtYXBwaW5nL2FlcyBjb21tYW5kKQogICMgICAgICB3ZSBjYW4gdGhlbiB1c2UgdGhlIHNhbWUgcGxvdCBvcmRlciBhYm92ZSB0byByZXdyaXRlIHRoZSBsYWJlbHMKICAjICAgICAgKGxpa2V3aXNlIHdlIGNvdWxkIGNoYW5nZSB0aGUgcGxvdCBvcmRlciBhbmQgb2YgY29ydXNlIHRoZSBjb2xvcnMuKQogIHNjYWxlX2NvbG9yX2Rpc2NyZXRlKGxhYmVscyA9IGMoIlNsdW1wIChjbSkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkZsb3cgKGNtKSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjI4ZHktQ29tcHJlc2lvbmFsIFN0cmVzcyAobVBhKSIpKSArIAogIAogIGdlb21fYm94cGxvdCgpICMgaW5zZXJ0IGNyZXRlIGEgcmVsYXRpdmUgZGVuc2l0eSBwbG90IAogIAoKYGBgCgojIyA1LjQuIFZpb2xpbiBQbG90IEV4YW1wbGUKCkhvdyBhYm91dCBsZXZlcmFnaW5nIGEgInZpb2xpbiIgcGxvdD8gIEEgdmlvbGluIHBsb3QncyB3aWR0aCBzd2VsbHMgaW4gYXJlYXMgd2l0aCBtb3JlIG9ic2VydmF0aW9ucyBhbmQgY29udHJhY3RzIHdpdGggc3BhcnNlciBkYXRhIHNvIGl0IGlzIGxpa2UgbG9va2luZyBhdCBhIHByb2JhYmlsaXR5IGRpc3RyaWJ1dGlvbi4KCgpgYGB7cn0KCmdncGxvdChkYXRhID0gY29uY3JldGVfaW5kZXBlbmRlbnQpICsgICAgICAjIEVESVQgQ2hhbmdpbmcgZGF0YWZyYW1lCiAgCiAgdGhlbWVfYncoICkgKyAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIGNoYW5naW5nIHRoZSBwbG90dGluZyB0aGVtZQogIAogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF9ibGFuaygpKSArICAgIyBhZGRpbmcgYW4gZXh0cmEgdHJhaXQgdG8gdGhlIHgtYXhpcwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyB0byBub3QgcHJpbnQgbGFiZWxzIG9uIHRoZSB4LWF4aXMgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjICh0aGUgbGFiZWxzIG92ZXJsYXAgYW5kIGRvZXNuJ3QgbG9vawogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBwcmV0dHkuLi4pCiAgCiAgYWVzKHkgICAgICA9IFZhbHVlLCAgICAgICAgICAgICAgICAgICAgICMgbWFwIHktYXhpcyB2YWx1ZQogICAgICB4ICAgICAgPSBQYXJhbWV0ZXIsICAgICAgICAgICAgICAgICAjIG1hcCB4LWF4aXMgdmFsdWUKICAgICAgY29sb3IgID0gUGFyYW1ldGVyKSArICAgICAgICAgICAgICAgIyBtYXAgY29sb3JzIGZvciBkaWZmZXJlbnQgcXVhbGl0eQogIAogIGdndGl0bGUobGFiZWwgICAgPSAiWWVoIFN1cGVycGxhc3RpY2l6ZXIgVGVzdHMiLAogICAgICAgICAgc3VidGl0bGUgPSAiQ29uY3JldGUgVGVzdCBDb21wb25lbnRzIikgKyAjIEN1c3RvbSBUaXRsZQogIAogIHlsYWIoZXhwcmVzc2lvbignQW1vdW50IChrZyBtJ14tMyoiKSIpKSArICMgIENoYW5naW5nIEN1c3RvbSBBeGlzIExhYmVsCgogIGdlb21fdmlvbGluKHNjYWxlPSJ3aWR0aCIpICMgRURJVDogY2hhbmdlIHRvIGEgdmlvbGluIHBsb3QgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyAgIHRoZSB3aWR0aCBhcmd1bWVudCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIGdpdmVzIGV2ZXJ5IHBsb3QgdGhlIHNhbWUgd2lkdGgKICAKCmBgYAphbmQuLi4KYGBge3J9CgpnZ3Bsb3QoZGF0YSA9IGNvbmNyZXRlX2RlcGVuZGVudCkgKyAgICAgICMgRURJVCBDaGFuZ2luZyBkYXRhZnJhbWUKICAKICB0aGVtZV9idyggKSArICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgY2hhbmdpbmcgdGhlIHBsb3R0aW5nIHRoZW1lCiAgCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCkpICsgICAjIGFkZGluZyBhbiBleHRyYSB0cmFpdCB0byB0aGUgeC1heGlzCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIHRvIG5vdCBwcmludCBsYWJlbHMgb24gdGhlIHgtYXhpcyAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgKHRoZSBsYWJlbHMgb3ZlcmxhcCBhbmQgZG9lc24ndCBsb29rCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIHByZXR0eS4uLikKICAKICBhZXMoeSAgICAgID0gVmFsdWUsICAgICAgICAgICAgICAgICAgICAgIyBtYXAgeS1heGlzIHZhbHVlCiAgICAgIHggICAgICA9IFBhcmFtZXRlciwgICAgICAgICAgICAgICAgICMgbWFwIHgtYXhpcyB2YWx1ZQogICAgICBjb2xvciAgPSBQYXJhbWV0ZXIpICsgICAgICAgICAgICAgICAjIG1hcCBjb2xvcnMgZm9yIGRpZmZlcmVudCBxdWFsaXR5CiAgCiAgZ2d0aXRsZShsYWJlbCAgICA9ICJZZWggU3VwZXJwbGFzdGljaXplciBUZXN0cyIsCiAgICAgICAgICBzdWJ0aXRsZSA9ICJDb25jcmV0ZSBUZXN0IFJlc3VsdHMiKSArICMgQ3VzdG9tIFRpdGxlCiAgCiAgeWxhYigiVmFsdWVzIikgKwoKICAjIE5FVzogSXQgc2F5cyBzY2FsZSBjb2xvciBidXQgImNvbG9yIiBpcyBob3cgd2UgYXJlIGRpc3Rpbmd1aXNoaW5nCiAgIyAgICAgIG91dCBib3hwbG90cyAoYXMgc2VlbiBpbiB0aGUgbWFwcGluZy9hZXMgY29tbWFuZCkKICAjICAgICAgd2UgY2FuIHRoZW4gdXNlIHRoZSBzYW1lIHBsb3Qgb3JkZXIgYWJvdmUgdG8gcmV3cml0ZSB0aGUgbGFiZWxzCiAgIyAgICAgIChsaWtld2lzZSB3ZSBjb3VsZCBjaGFuZ2UgdGhlIHBsb3Qgb3JkZXIgYW5kIG9mIGNvcnVzZSB0aGUgY29sb3JzLikKICBzY2FsZV9jb2xvcl9kaXNjcmV0ZShsYWJlbHMgPSBjKCJTbHVtcCAoY20pIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJGbG93IChjbSkiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIyOGR5LUNvbXByZXNpb25hbCBTdHJlc3MgKG1QYSkiKSkgKyAKICAKCiAgZ2VvbV92aW9saW4oc2NhbGU9IndpZHRoIikgIyBFRElUOiBjaGFuZ2UgdG8gYSB2aW9saW4gcGxvdCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjICAgdGhlIHdpZHRoIGFyZ3VtZW50IAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgZ2l2ZXMgZXZlcnkgcGxvdCB0aGUgc2FtZSB3aWR0aCAgCgpgYGAKVGhpcyBpcyBiYXNpY2FsbHkgdGhlIGFib3ZlICJkZW5zaXR5IiBwbG90IGJ1dCAibG9va2luZyBkb3duIiBhcyB3aXRoIGEgYm94IHBsb3QuICBBbHNvIGhlcmUgd2UgYXJlIHRyaW1taW5nIHRoZSBwbG90IHNvIHRoYXQgd2hlbiB3ZSBsZWF2ZSB0aGUgcmFuZ2Ugb2YgYW55IG9mIHRoZSBkYXRhIHBvaW50cywgdGhlICJ2aW9saW5zIiBhcmUgdHJ1bmNhdGVkLgoKIyMgNS41LiBTdGFja2VkIENvbHVtbiBvciBCYXIgUGxvdCBFeGFtcGxlCgpXZSBhbHNvIGNhbiBkbyBiYXIgcGxvdHMgb3Igc3RhY2tlZCBjb2x1bW4gcGxvdHMuICBUaGUgb25lIHByb2R1Y2VkIGhlcmUgc2hvd3MgdGhlIGNvbWJpbmVkIGNvbXBvbmVudHMgYnkgdGVzdCB1bml0LgoKYGBge3J9CgpnZ3Bsb3QoZGF0YSA9IGNvbmNyZXRlX2luZGVwZW5kZW50KSArICAgICAgIyBFRElUIENoYW5naW5nIGRhdGFmcmFtZQogIAogIHRoZW1lX2J3KCApICsgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBjaGFuZ2luZyB0aGUgcGxvdHRpbmcgdGhlbWUKCiAgCiAgYWVzKHggICAgID0gVGVzdF9OdW1iZXIsCiAgICAgIHkgICAgID0gVmFsdWUsCiAgICAgIGZpbGwgID0gUGFyYW1ldGVyKSArICAgICAgICAgICAgICAgIyBtYXAgY29sb3JzIGZvciBkaWZmZXJlbnQgcXVhbGl0eQogIAogIGdndGl0bGUobGFiZWwgICAgPSAiWWVoIFN1cGVycGxhc3RpY2l6ZXIgVGVzdHMiLAogICAgICAgICAgc3VidGl0bGUgPSAiQ29uY3JldGUgVGVzdCBDb21wb25lbnRzIikgKyAjIEN1c3RvbSBUaXRsZQogIAogIHlsYWIoZXhwcmVzc2lvbignQW1vdW50IChrZyBtJ14tMyoiKSIpKSArICMgIENoYW5naW5nIEN1c3RvbSBBeGlzIExhYmVsCgogIGdlb21fY29sKHBvc2l0aW9uID0gInN0YWNrIiwgICMgbmV3LCBjcmVhdGUgYSBzdGFjZWtkIGNvbHVtbiBncmFwaCAKICAgICAgICAgICB3aWR0aCAgICA9IDEuMCAgICApICAjIHdpdGggbm8gc3BhY2UgYmV0d2VlbiBjb2x1bW5zCgpgYGAKCgojIDYuIENvcnJlbGF0aW9uIG9mIFZhcmlhYmxlcwoKIyMgNi4xLiBDb3JyZWxhdGluZyBhbmQgdGhlbiBGaXR0aW5nIENlbWVudCB0byBDb21wcmVzc2l2ZSBTdHJlbmd0aAoKTGV0J3Mgc3RhcnQgYnkgZG9pbmcgYSAic2ltcGxlIiIgcGxvdCAuICBJbiB0aGlzIGNhc2Ugc2luY2UgSSBhbHJlYWR5IGtub3cgdGhlIGFuc3dlciBiZWNhdXNlIHRoZSBzcHJlYWRzaGVldCBhbHNvIGhhcyBhIHRhYmxlIG9mIGhvdyB3ZWxsIG91ciBpbmRlcGVuZGVudCB2YXJpYWJsZXMgY29ycmVsYXRlIGFnYWluc3QgdGhlIGRlcGVuZGVudCB2YXJpYWJsZXMgKGUuZy4sIFNsdW1wLCBGbG93LCBvciBpbiBvdXIgY2FzZSBTdHJlbmd0aCkuICBUaGUgQ2VtZW50IGNvcnJlbGF0ZXMgdGhlIGJlc3QgYWdhaW5zdCBDb21wcmVzc2l2ZSBTdHJlbmd0aCAoT0ssIHRydXRoIGJlIHRvbGQsIGl0IGNvcnJlbGF0ZXMgdGhlIGxlYXN0IGJhZGx5KS4KCldlIGNhbiBhY3R1YWxseSBkbyB0aGlzIHdpdGggYSBjb3JyZWxhdGUgZnVuY3Rpb24sIFtjb3IoKV0oaHR0cHM6Ly93d3cucmRvY3VtZW50YXRpb24ub3JnL3BhY2thZ2VzL3N0YXRzL3ZlcnNpb25zLzMuNC4zL3RvcGljcy9jb3IpLi4uCgpUbyBncmFiIGEgdmFsdWUgaW4gdGhlIHRhYmxlICJjb25jcmV0ZSIgd2UgY2FsbCB0aGUgZGF0YSBmcmFtZSAoY29uY3JldGUpIGFuZCB0aGUgdmFyaWFibGUgbmFtZSAoQ2VtZW50IG9yIFdhdGVyIHZzIENvbXByZXNzaXZlX1N0cmVuZ3RoXzI4ZHkpLCBzZXBhcmF0aW5nIHRoZSBmcmFtZSBhbmQgdmFyaWFibGUgbmFtZXMgYnkgYSAkIHNpZ24uCgoKYGBge3J9CgpwcmludCgiQ2VtZW50IHZzIENvbXByZXNzaXZlIFN0cmVuZ3RoIENvcnJlbGF0aW9uLCByIikKCmNvcih4ID0gY29uY3JldGUkQ2VtZW50LCAgICAgICAgICAgICAgICAgICAgIyB0aGUgeC12YWx1ZSAKICAgIHkgPSBjb25jcmV0ZSRDb21wcmVzc2l2ZV9TdHJlbmd0aF8yOGR5LCAjIHRoZSB5LXZhbHVlCiAgICBtZXRob2QgPSAicGVhcnNvbiIgICAgICAgICAgICAgICAgICAgICAgIyBtZXRob2Qgb2YgY29ycmVsYXRpb24KICAgICkKCmBgYAoKb3IgaWYgeW91IGxpa2UgdG8gZG8gZXZlcnl0aGluZyBhdCBvbmNlLi4uCgpgYGB7cn0KCiMgY2FsY3VsYXRlIGFsbCBjb3JyZWxhdGlvbiB2YWx1ZXMgYWdhaW5zdCBlYWNoIG90aGVyCgpjb3JyZWxhdGlvbl9tYXRyaXggPSBjb3IoeCAgICAgID0gY29uY3JldGUsICMgdXNpbmcgb3VyIGRhdGFmcmFtZSB0byBjb3JyZWxhdGUgZXZ5dGhpbmcKICAgICAgICAgICAgICAgICAgICAgICAgIG1ldGhvZCA9ICJwZWFyc29uIiApCgp0YmxfZGYoY29ycmVsYXRpb25fbWF0cml4KQoKYGBgCgpMb3RzIG9mIG51bWJlcnMuLi4gbm90IGFsbCB0aGF0IGluc2lnaHRmdWwgb24gdGhlaXIgb3duLi4uIAoKWW91IGFsc28gY2FuIGdyYXBoIHRoZSBsb29rLW4tZmVlbCBvZiB3aGF0IGFsbCBvZiB0aGUgZGlmZmVyZW50IGNvcnJlbGF0aW9ucyBhcmUuLi4gKGl0IHdvcmtzIGJlc3Qgd2l0aCBhIG11Y2ggc21hbGxlciBudW1iZXIgb2YgdmFyaWFibGVzKQoKYGBge3J9CgogICMgZHJhdyBhIGNvb3JlbGF0aW9uIGdyYXBoaWMuLi4KCiAgY29ycnBsb3QoY29yciAgID0gY29ycmVsYXRpb25fbWF0cml4LAogICAgICAgICAgIHR5cGUgICA9ICJ1cHBlciIpCgpgYGAKV2UgY2FuIG5vdyBzZWUgZm9yIGV4YW1wbGUgdGhhdCBjZW1lbnQsIHNsYWcsIGFuZCBmbHkgYXNoIGFtb3VudHMgaGF2ZSBhIG5vbWluYWwgYnV0IG5vdCB0aHJpbGxpbmcgY29ycmVsYXRpb24gdG8gY29tcHJlc3Npb24gc3RyZW5ndGggd2hpbGUgd2F0ZXIgaGFzIGEgZ29vZCBjb3JyZWxhdGlvbiB3aXRoIHRoZSByZXN1bHRpbmcgc2x1bXAgdmFsdWVzLiAgT25lIHRoaW5nIHRoYXQgdGhpcyBkb2VzICpub3QqIHNob3cgaXMgaG93IHdlbGwgdGhlc2UgcGFyYW1ldGVycyBwbGF5IHdpdGggb3RoZXIgcGFyYW1ldGVycy4gIEFzIHdlJ2xsIHNlZSB3aGVuIGFsbCBvZiBvdXIgaW5kZXBlbmRlbnQgdmFsdWVzIGFyZSB3b3JraW5nIHRvZ2V0aGVyIHdlJ2xsIGRpc2NvdmVyIHRoYXQgY2VtZW50IGFuZCB3YXRlciwgZm9sbG93ZWQgYnkgZmx5IGFzaCBhbmQgY29hcnNlIGFnZ3JlZ2F0ZXMgd2lsbCwgdG9nZXRoZXIsIGNvbnRyaWJ1dGUgdGhlIG1vc3Qgb2Ygb3VyIGluZGVwZW5kZW50IHBhcmFtZXRlcnMgaW4gY2FsY3VsYXRpbmcgdGhlIGNvbXByZXNzaXZlIHN0cmVuZ3RoLgoKIyMgNi4yLiBTY2F0dGVyIFBsb3QgRXhhbXBsZQoKQnV0IGZvciBub3csIGxldCdzIHBsb3QgcGxvdCB0aGUgQ2VtZW50IGFtb3VudCBhZ2FpbnN0IENvbXByZXNzaXZlIFN0cmVuZ3RoCgpgYGB7cn0KCiMgTWFraW5nIGEgc2ltcGxlIFgtWSBzY2F0dGVycGxvdC4KCmdncGxvdChkYXRhID0gY29uY3JldGUpICsgICAgICAgICAgICAgICAgIyBpbnZva2UgZ3JhcGhpY3MgZW52aXJvbm1lbnQgdXNpbmcgYSBnaXZlbiBkYXRhZnJhbWUKICAKICB0aGVtZV9idyggKSArICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBjaGFuZ2luZyB0aGUgcGxvdHRpbmcgdGhlbWUKICAKICBhZXMoeCAgICAgID0gQ2VtZW50LCAgICAgICAgICAgICAgICAgICAgICAgIyB4LXZhbHVlCiAgICAgIHkgICAgICA9IENvbXByZXNzaXZlX1N0cmVuZ3RoXzI4ZHkpICsgICMgeS12YWx1ZQoKICBnZ3RpdGxlKCJZZWggU3VwZXJwbGFzdGljaXplciBUZXN0cyIpICsgICAgIyBDdXN0b20gVGl0bGUKICAKICB4bGFiKGV4cHJlc3Npb24oJ0NlbWVudCBBbW91bnQgKGtnIG0nXjMqIikiKSkgKyAgICMgeC1sYWJlbAogIHlsYWIoIjI4LWR5IENvbXByZXNzaXZlIFN0cmVuZ3RoIChNUGEpIikgICAgICArICAgIyB5LWxhYmVsCgogIGdlb21fcG9pbnQoY29sb3VyPSJncmV5IikgICAjIEVESVQ6IHBsb3QgcG9pbnRzIHRoZSBjb2xvciBrZXl3b3JkIHBhcnQgd2FzCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgICAgICAgd3JpdGVuIGJ5IGFuIGFuZ2xvcGhpbGUhCgpgYGAKCkhlcmUncyBhIGN1dGUgdHJpY2s6ICBDb3VsZCB3ZSBjb2xvciB0aG9zZSBkb3RzIGJ5IGEgdmFyaWFibGU/CgpTdXJlIQoKYGBge3J9CgojIE1ha2luZyBhIHNpbXBsZSBYLVkgc2NhdHRlcnBsb3Qgbm93IGNvbG91cmVkIGJ5IGFub3RoZXIgcGFyYW1ldGVyCgpnZ3Bsb3QoZGF0YSA9IGNvbmNyZXRlKSArICAgICAgICAgICAgICAgICMgaW52b2tlIGdyYXBoaWNzIGVudmlyb25tZW50IHVzaW5nIGEgZ2l2ZW4gZGF0YWZyYW1lCiAgCiAgdGhlbWVfYncoICkgKyAgICAgICAgICAgICAgICAgICAgICAgICAgICMgY2hhbmdpbmcgdGhlIHBsb3R0aW5nIHRoZW1lCiAgCiAgYWVzKHggICAgICA9IENlbWVudCwgICAgICAgICAgICAgICAgICAgICAgICMgeC12YWx1ZQogICAgICB5ICAgICAgPSBDb21wcmVzc2l2ZV9TdHJlbmd0aF8yOGR5LCAgICAjIHktdmFsdWUKICAgICAgY29sb3IgID0gU3VwZXJwbGFzdGljaXplcikgICAgICAgICAgKyAgIyBBREQ6IHdlIGNhbiBjb2xvciBieSBhIHZhcmlhYmxlIQoKICBnZ3RpdGxlKCJZZWggU3VwZXJwbGFzdGljaXplciBUZXN0cyIpICsgICAgIyBDdXN0b20gVGl0bGUKICAKICB4bGFiKGV4cHJlc3Npb24oJ0NlbWVudCBBbW91bnQgKGtnIG0nXjMqIikiKSkgKyAgICMgeC1sYWJlbAogIHlsYWIoIjI4LWR5IENvbXByZXNzaXZlIFN0cmVuZ3RoIChNUGEpIikgICAgICArICAgIyB5LWxhYmVsCgogIGdlb21fcG9pbnQoKSArICAjIHBsb3QgcG9pbnRzIAogIHNjYWxlX2NvbG9yX2Rpc3RpbGxlcihwYWxldHRlID0gIlNwZWN0cmFsIikgIyBORVc6IHBpY2sgYSBjdXN0b20gImNvbG91ciIgcGFsYXRlLgoKYGBgCgpMb3ZlIG92ZXJraWxsIHdpdGhvdXQgYW55IGRpc3RpbmN0IG51bWVyaWNhbCBzY29yZSBhbmQgbG9vayBhdCBob3cgZXZlcnl0aGluZyBpbiB5b3VyIGRhdGEgc2V0IGNvcnJlbGF0ZXMgd2l0aCBldmVyeSBvdGhlciB2YXJpYWJsZXMuLi4/IAoKVHJ5IFtwYWlycygpXShodHRwczovL3d3dy5yZG9jdW1lbnRhdGlvbi5vcmcvcGFja2FnZXMvZ3JhcGhpY3MvdmVyc2lvbnMvMy41LjEvdG9waWNzL3BhaXJzKQoKKEkgbGlrZSB0aGUgY29ycnBsb3QgZnVuY3Rpb24gYmV0dGVyISkKCmBgYHtyfQoKIyB3YXkgdG9vIG1hbnkgdGlueSBwbG90cyEKCnBhaXJzKHggICA9IGNvbmNyZXRlLCAjIGRvIGV2ZXJ5dGhpbmcgaW4gdGhlIGRhdGFmcmFtZQogICAgICBwY2ggPSAiLiIpICAgICAgIyBwbG90IGRvdHMgKHRoZSBkZWZhdWx0IGlzIGNpcmNsZXMpCgpgYGAKCihPYnZpb3VzbHkgdGhlIG1vcmUgdmFyaWFibGVzIGluIHlvdXIgZGF0YWZyYW1lIHRoZSBtZXNzaWVyIGl0IGdldHMhKQoKCgojIyA2LjMuIENyZWF0aW5nIG91ciBsaW5lYXIgbW9kZWwgYW5kICJjYWxpYnJhdGluZyIgaXQKCldlIHdlcmVuJ3QgYWxsIHRoYXQgdGhyaWxsZWQgd2l0aCB0aGUgY29ycmVsYXRpb24gYmV0d2VlbiB0aGVzZSBjb21wb25lbnRzIGFuZCBzdHJlbmd0aCBidXQgbGV0J3MgZ28gYWhlYWQgYW5kIGRlbW9uc3RyYXRlIGEgcmVncmVzc2lvbi4KCkJ1dCBsZXQncyBtb3ZlIG9uIGFuZCBjcmVhdGUgYSByZWdyZXNzaW9uIG1vZGVsIGZyb20gdGhpcy4gIAoKSGVyZSB3ZSB3aWxsIHVzZSB0aGUgW2xtKCldKGh0dHBzOi8vd3d3LnJkb2N1bWVudGF0aW9uLm9yZy9wYWNrYWdlcy9zdGF0cy92ZXJzaW9ucy8zLjQuMy90b3BpY3MvbG0pIChsaW5lYXIgbW9kZWwpIGZ1bmN0aW9uIGZyb20gdGhlIE1BU1MgcGFja2FnZS4KCkZvciB0aGUgcmVncmVzc2lvbiBmb3JtdWxhIAoKJFx3aWRlaGF0e3l9KHgpID0ge1xhbHBoYV8wfSt7XGFscGhhXzF9XCB4JAoKb3IKCiRcd2lkZWhhdHtTdHJlbmd0aH0oY29uY3JldGUpID0ge1xhbHBoYV8wfSt7XGFscGhhXzF9XCBjb25jcmV0ZSQKCnRoZSAicHJvdG90eXBlIiAoZm9ybXVsYSkgZm9yIHRoZSBmdW5jdGlvbiBpcyB3cml0dGVuIGFzIC4uLiAKCiJZIH4gWCIgKHdpdGggdGhlIHktaW50ZXJjZXB0IGltcGxpY2l0IGluIHRoZSBmb3JtdWxhLi4uIHlvdSBkb24ndCBwdXQgaXQgaW4gYnV0IGl0J2xsIGJlIHRoZXJlIHdoZW4geW91J3JlIGRvbmUuKQoKVGhlIGFib3ZlIHN5bnRheCBpcyB3b3JrcyBsaWtlIHRoaXMuLi4uCgpEZXBlbmRlbnQgVmFyaWFibGUgIFt+IGlzIGEgZnVuY3Rpb24gb2YgXSBJbmRlcGVuZGVudCBWYXJpYWJsZSBbYW5kIGFueSBvdGhlciBwYXJhbWV0ZXIgeW91IG5lZWQgZ2V0cyBhZGRlZCB3aXRoIGEgcGx1c10KCklmIHRoaXMgd2VyZSBhICRcd2lkZWhhdHt5fSh4KT17XGFscGhhXzB9K3tcYWxwaGFfMH1cIHheMyQsIHRoZW4gdGhlIHByb3RvdHlwZSBmb3IgdGhlIGZ1bmN0aW9uIHdvdWxkIGJlIHkgfiB4XjMKClRoaXMgd2lsbCBob3BlZnVsbHkgbWFrZSBtb3JlIHNlbnNlIGFzIHdlIGNvbnRpbnVlIQoKKihsbSBhbmQgc2ltaWxhciBsaW5lYXIgcmVncmVzc2lvbiBmdW5jdGlvbnMgZG9uJ3QgcGxheSB3ZWxsIHdpdGggdW5pdHMuKSoKCmBgYHtyfQoKbGluZWFyX21vZGVsLlNfdl9jID0gIGxtKGZvcm11bGEgPSBDb21wcmVzc2l2ZV9TdHJlbmd0aF8yOGR5IH4gQ2VtZW50LCAjIHlvdXIgZm9ybXVsYSB5IH4geAogICAgICAgICAgICAgICAgICAgICAgICAgZGF0YSAgICA9IGNvbmNyZXRlKSAgICAgICAgICAgICAgICAgICAgICAgICAgICMgdGhlIGRhdGEgZnJhbWUKYGBgCgpMZXQncyBzZWUgd2hhdCB3ZSBoYXZlLi4uICBUaGlzIHN1bW1hcnkgY29tbWFuZCB3aWxsIHByb3ZpZGUgdGhlIGRldGFpbHMgb2YgdGhlIGxtKCkgZnVuY3Rpb24ncyBpbXBvcnRhbnQgcmVzdWx0cwoKRm9yIHVzIHdlIHdhbnQgdG8gc2VlIHRoZSBZLUludGVyY2VwdCBbdGhlIChJbnRlcmNlcHQpIHVuZGVyICJFc3RpbWF0ZSJdIGFuZCB0aGUgc2xvcGUgdGhhdCBnb2VzIHdpdGggb3VyIGluZGVwZW5kZW50IHZhbHVlICgiQ29uY3JldGUiIHVuZGVyICJFc3RpbWF0ZSIpIAoKVGhlIFN0YW5kYXJkIEVycm9yIG9mIHRoZSBFc3RpbWF0ZSBpcyB0aGVyZSAoUmVzaWR1YWwgU3RhbmRhcmQgRXJyb3IpIGFzIGlzIHRoZSBDb2VmZmljaWVudCBvZiBEZXRlcm1pbmF0aW9uIChNdWx0aXBsZSBSLXNxdWFyZWQpCgpXZSdsbCB0YWxrIGFib3V0IGEgZmV3IG9mIHRoZSBvdGhlciBmZWF0dXJlcyB3aGVuIHdlIGRvIHRoZSBsYXJnZXIgbXVsdGl2YXJpYXRlIHJlZ3Jlc3Npb24KCmBgYHtyfQoKIHN1bW1hcnkob2JqZWN0ID0gbGluZWFyX21vZGVsLlNfdl9jKQoKYGBgCgpJbiB0aGUgYWJvdmUgb3V0cHV0LCB0aGUgYXN0ZXJpc2sgaWRlbnRpZnkgdGhlIG1vc3Qgc2lnbmlmaWNhbnQgaW5kZXBlbmRlbnQgdmFyaWFibGVzLiAgIEhlcmUgaXQncyB0cml2aWFsIGV2ZW4gdGhvdWdoIHRoaXMgaXMgYSB0ZXJyaWJsZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBjZW1lbnQgYW5kIHN0cmVuZ3RoLiAgTGF0ZXIgd2Ugd2lsbCB1c2UgYWxsIG9mIG91ciBhdmFpbGFibGUgaW5kZXBlbmRlbnQgdmFyaWFibGVzIGFuZCB0aGUgdXNlIG9mIHRoZXNlIGFzdGVyaXNrcyB3aWxsIGJlY29tZSBtb3JlIGltcG9ydGFudC4KCgpXYW50IHRvIHBsb3QgaXQ/ICAKCkdvb2QgbmV3cz8gIAoKTGlrZSBFeGNlbCwgeW91IGhhdmUgc29tZSBhdXRvbWF0ZWQgZmVhdHVyZXMgdG8gZ2l2ZSB5b3UgcXVpY2sgc2F0aXNmYWN0aW9uIGFuZCBoYXBwaW5lc3MuICBNb3JlIHN0aWxsLCBpdCB3aWxsIGdpdmUgeW91IGNvbmZpZGVuY2UgbGltaXRzLgoKRm9yIHRoaXMgd2UgdXNlIGFuIGV4dGVuc2lvbiB0byB0aGUgZ3JhcGhpY3MgcGFja2FnZSBjYWxsZWQgW2dlb21fc21vb3RoKCldKGh0dHBzOi8vZ2dwbG90Mi50aWR5dmVyc2Uub3JnL3JlZmVyZW5jZS9nZW9tX3Ntb290aC5odG1sKQoKYGBge3J9CgojIE1ha2luZyBhIHNpbXBsZSBYLVkgc2NhdHRlcnBsb3QgYW5kIGFkZGluZyBhIHJlZ3Jlc3Npb24gdG8gaXQKCmdncGxvdChkYXRhID0gY29uY3JldGUpICsgICAgICAgICAgICAgICAgIyBpbnZva2UgZ3JhcGhpY3MgZW52aXJvbm1lbnQgdXNpbmcgYSBnaXZlbiBkYXRhZnJhbWUKICAKICB0aGVtZV9idyggKSArICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBjaGFuZ2luZyB0aGUgcGxvdHRpbmcgdGhlbWUKICAKICBhZXMoeCAgICAgID0gQ2VtZW50LCAgICAgICAgICAgICAgICAgICAgICAgIyB4LXZhbHVlCiAgICAgIHkgICAgICA9IENvbXByZXNzaXZlX1N0cmVuZ3RoXzI4ZHkpICsgICMgeS12YWx1ZQoKICBnZ3RpdGxlKCJZZWggU3VwZXJwbGFzdGljaXplciBUZXN0cyIpICsgICAgIyBDdXN0b20gVGl0bGUKICAKICB4bGFiKGV4cHJlc3Npb24oJ0NlbWVudCBBbW91bnQgKGtnIG0nXi0zKiIpIikpICsgICAjIHgtbGFiZWwKICB5bGFiKCIyOC1keSBDb21wcmVzc2l2ZSBTdHJlbmd0aCAoTVBhKSIpICAgICAgKyAgICMgeS1sYWJlbAoKICBnZW9tX3BvaW50KGNvbG91cj0iZGFya2dyZXkiKSArICAjIHBsb3QgcG9pbnRzCiAgZ2VvbV9zbW9vdGgobWV0aG9kICA9ICJsbSIsICAgICMgdXNlIGEgc2ltcGxlIGxpbmFyIG1vZGVsCiAgICAgICAgICAgICAgZm9ybXVsYSA9IHkgfiB4LCAgICMgbG0tc3R5bGUgZm9ybXVsYQogICAgICAgICAgICAgIHNlICAgICAgPSBUUlVFLCAgICAjIHNwbGF5IENvbmZpZGVuY2UgSW50ZXJ2YWxzCiAgICAgICAgICAgICAgbGV2ZWwgICA9IDAuOTUsICAgICMgQ29uZmlkZW5lIExldmVsIHRvIE1hcCBPdXQKICAgICAgICAgICAgICBjb2xvdXIgID0gImJsYWNrIiwgIyByZWdyZXNzaW9uIGxpbmUgY29sb3IKICAgICAgICAgICAgICBzaXplICAgID0gMC41KSAgICAgIyBsaW5lIHRoaWNrbmVzcwoKYGBgCgpUaGUgbGluZSBoZXJlIGxvb2tzIGxpa2UgYSBwb3NpdGl2ZSBjb3JyZWxhdGlvbiBiZXR3ZWVuIHRoZSBjZW1lbnQgYW1vdW50IGFuZCB0aGUgcmVzdWx0aW5nIHN0cmVuZ3RoLgoKTGV0J3MgdHJ5IHdhdGVyOgoKCmBgYHtyfQoKIyBnZXR0aW5nIHRoZSBsaW5lYXIgbW9kZWwKCgpsaW5lYXJfbW9kZWwuU192X3cgPSAgbG0oZm9ybXVsYSA9IENvbXByZXNzaXZlX1N0cmVuZ3RoXzI4ZHkgfiBXYXRlciwgIyB5b3VyIGZvcm11bGEgeSB+IHgKICAgICAgICAgICAgICAgICAgICAgICAgIGRhdGEgICAgPSBjb25jcmV0ZSAgICkgICAgICAgICAgICAgICAgICAgICAgICAgICAjIHRoZSBkYXRhIGZyYW1lCgpzdW1tYXJ5KGxpbmVhcl9tb2RlbC5TX3ZfdykKYGBgCgpgYGB7cn0KCiMgTWFraW5nIGEgc2ltcGxlIFgtWSBzY2F0dGVycGxvdCBhbmQgYWRkaW5nIGEgcmVncmVzc2lvbiB0byBpdAoKZ2dwbG90KGRhdGEgPSBjb25jcmV0ZSkgKyAgICAgICAgICAgICAgICAjIGludm9rZSBncmFwaGljcyBlbnZpcm9ubWVudCB1c2luZyBhIGdpdmVuIGRhdGFmcmFtZQogIAogIHRoZW1lX2J3KCApICsgICAgICAgICAgICAgICAgICAgICAgICAgICAjIGNoYW5naW5nIHRoZSBwbG90dGluZyB0aGVtZQogIAogIGFlcyh4ICAgICAgPSBXYXRlciwgICAgICAgICAgICAgICAgICAgICAgIyB4LXZhbHVlCiAgICAgIHkgICAgICA9IENvbXByZXNzaXZlX1N0cmVuZ3RoXzI4ZHkpICsgICMgeS12YWx1ZQoKICBnZ3RpdGxlKCJZZWggU3VwZXJwbGFzdGljaXplciBUZXN0cyIpICsgICAgIyBDdXN0b20gVGl0bGUKICAKICB4bGFiKGV4cHJlc3Npb24oJ1dhdGVyIEFtb3VudCAoa2cgbSdeLTMqIikiKSkgKyAgIyB4LWxhYmVsCiAgeWxhYigiMjgtZHkgQ29tcHJlc3NpdmUgU3RyZW5ndGggKE1QYSkiKSAgICAgICsgICAjIHktbGFiZWwKCiAgZ2VvbV9wb2ludChjb2xvdXI9ImRhcmtibHVlIikgKyAgIyBwbG90IHBvaW50cwogIAogIGdlb21fc21vb3RoKG1ldGhvZCAgPSAibG0iLCAgICAjIHVzZSBhIHNpbXBsZSBsaW5hciBtb2RlbAogICAgICAgICAgICAgIGZvcm11bGEgPSB5IH4geCwgICAjIGxtLXN0eWxlIGZvcm11bGEKICAgICAgICAgICAgICBzZSAgICAgID0gVFJVRSwgICAgIyBzcGxheSBDb25maWRlbmNlIEludGVydmFscwogICAgICAgICAgICAgIGxldmVsICAgPSAwLjk1LCAgICAjIENvbmZpZGVuZSBMZXZlbCB0byBNYXAgT3V0CiAgICAgICAgICAgICAgY29sb3VyICA9ICJibHVlIiwgICMgcmVncmVzc2lvbiBsaW5lIGNvbG9yCiAgICAgICAgICAgICAgZmlsbCAgICA9ICJjeWFuIiwgICMgTkVXOiBmaWxsIGZvciBjb25maWRlbmNlIGxpbWl0cwogICAgICAgICAgICAgIHNpemUgICAgPSAwLjUpICAgICAjIGxpbmUgdGhpY2tuZXNzCgpgYGAKCkxvb2tpbmcgdXAgYmFjayB0aGUgdGFibGVzIG5vbmUgb2YgdGhlIHZhcmlhYmxlcyAKCiMgNy4gTXVsdGl2YXJpYXRlIExpbmVhciBSZWdyZXNzaW9uCgpBbmQgbm93IHdlJ3JlIGdvaW5nIHRvIGRvIHNvbWV0aGluZyBhYm91dCB0aGF0IQoKV2UncmUgbm93IGdvaW5nIHRvIHVzZSBub3QganVzdCBvbmUgaW5kZXBlbmRlbnQgdmFyaWFibGUuLi4gYnV0IGFsbCA3IG9mIHRoZW0hCgpUaGUgZ29vZCBuZXdzIGlzIHRoYXQgaXQgZm9sbG93cyB0aGUgc2FtZSBmb3JtIGFzIHRoZSBzaW1wbGUgbGluZWFyIHJlZ3Jlc3Npb24uICBUaGlzIHRpbWUgd2Ugc3RyaW5nIGFsb25nIGFsbCBvZiBvdXIgaW5kZXBlbmRlbnQgdmFyaWFibGVzIHdpdGggaW4gb3VyIGZvcm11bGEgcHJvdG90eXBlLgoKT3VyIGZvcm11bGEgbm93IGhhcyBtdWx0aXBsZSBpbmRlcGVuZGVudCB2YWx1ZXMgYnV0IHN0aWxsIGZvbGxvd3MgdGhlIHNhbWUgc3R5bGUgb2Ygc29sdXRpb24uLi4KCiRcd2lkZWhhdHt5fShcbWF0aGJme3h9KSA9IHtcYWxwaGFfMH0re1xhbHBoYV8xfSB4XzEgKyB7XGFscGhhXzJ9IHhfMiArIHtcYWxwaGFfMn0geF8zICArIC4uLiAre1xhbHBoYV9ufSB4X24kIAoKCgpgYGB7cn0KCmxpbmVhcl9tb2RlbC5TX3ZfYWxsIDwtIGxtKGRhdGEgICAgPSBjb25jcmV0ZSwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgeW91ciBkYXRhIGZyYW1lCiAgICAgICAgICAgICAgICAgICAgICAgICAgIGZvcm11bGEgPSBDb21wcmVzc2l2ZV9TdHJlbmd0aF8yOGR5IH4gQ2VtZW50ICsgICMgeW91ciBmb3JtdWxhCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgU2xhZyArCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgRmx5X0FzaCArCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgV2F0ZXIgKwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFN1cGVycGxhc3RpY2l6ZXIgKwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEZpbmVfQWdncmVnYXRlcyArCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQ29hcnNlX0FnZ3JlZ2F0ZXMpICAKCgpgYGAKCkFuZCBoZXJlIGFyZSB0aGVzZSByZXN1bHRzLi4uIAoKYGBge3J9CgpzdW1tYXJ5KG9iamVjdCA9IGxpbmVhcl9tb2RlbC5TX3ZfYWxsKQoKYGBgCgpPdXIgcmVncmVzc2lvbiBjb2VmZmljaWVudHMgYXJlIHN0aWxsIGhlcmUgdW5kZXIgdGhlICJFc3RpbWF0ZSIgY29sdW1uIGFzIGFyZSBvdXIgU3RhbmRhcmQgRXJyb3Igb2Ygb3VyIEVzdGltYXRlIGFuZCBvdXIgQ29lZmYgb2YgRGV0ZXJtaW5hdGlvbi4KCkFsc28gd2UgY2FuIG5vdyB0YWtlIGEgZ29vZCBsb29rIGF0IHRob3NlIGFzdGVyaXNrcyBhdCB0aGUgZW5kIG9mIGxpbmUgd2l0aCB0aGUgcGFyYW1ldGVyIGNvZWZmaWNpZW50cy4gIFRoZXNlIGNhbiBleHBsYWluIHdoaWNoIGluZGVwZW5kZW50IHZhcmlhYmxlcyBkbyB0aGUgaGVhdmllc3QgbGlmdGluZyBpbiBvdXIgcmVncmVzc2lvbi4gVGhlIG1vcmUgYXN0ZXJpc2tzLCB0aGUgbW9yZSBpbXBvcnRhbnQgdGhlIGRlcGVuZGVudCB2YXJpYWJsZSBpcyB0byB0aGUgbGFyZ2VyIG11bHRpdmFyaWF0ZSByZWdyZXNzaW9uLiBIZXJlLCB3ZSBjYW4gc2VlIHRoYXQgdGhlIENlbWVudCBhbmQgV2F0ZXIgYXJlIGRvaW5nIG1vc3Qgb2YgdGhlICJ3b3JrIiBpbiBmaXR0aW5nIG91ciBzdWl0ZSBvZiBpbmRlcGVuZGVudCB2YXJpYWJsZXMgdG8gb3VyIGRlcGVuZGVudCB2YXJpYWJsZSBvZiBDb21wcmVzc2l2ZSBTdHJlbmd0aC4KCkZpbmFsbHkgdGhlcmUgaXMgdGhlIFAgcGFyYW1ldGVyIGZvciB3aGljaCB0aGUgc21hbGxlciBpdCBpcywgdGhlIGJldHRlciB3ZSBjYW4gc2F5IHRoYXQgdGhlIHJlbGF0aW9uc2hpcCB0aGF0IHdlJ3ZlIG1hZGUgd2l0aCBvdXIgcmVncmVzc2lvbiByZXByZXNlbnRzIG91ciBkZXBlbmRlbnQgdmFyaWFibGUuCgpOb3cuLi4gb24gdG8gbG9va2luZyBhdCBvdXIgcmVzdWx0cy4KCkhlcmUgaXMgd2hlcmUgdmlld2luZyB0aGUgcmVzdWx0cyBvZiB0aGUgcmVncmVzc2lvbiBpcyB0cmlja3kuCgpXZSBoYXZlIDcgaW5kZXBlbmRlbnQgdmFyaWFibGVzIGJ1dCB3ZSdkIGxpa2UgdG8gc2VlIHRoZSBpbXBhY3Qgb2YgdGhlIGZpdCBpZiBhbGwgNyB2YXJpYWJsZXMgb24gb3VyIHN0cmVuZ3RoIAoKV2hlbiBJIGRvIHRoaXMgSSBsaWtlIHRvIHBsb3QgdGhlIHRydWUgeSB2YWx1ZSBhZ2FpbnN0IG15IHJlZ3Jlc3Npb24geSh4MSx4Mix4MywuLikKClNvIHRvIGRvIHRoaXMgSSB3aWxsIHRha2UgdGhlIGZpdHRlZCB2YWx1ZXMgb2YgeSBhbmQgcGxvdCB0aGVtIGFnYWluc3QgdGhlIG9yaWdpbmFsIHZhbHVlcyBvZiB5CgpHZXR0aW5nIHRoZSBmaXR0ZWQgdmFsdWVzIGlzIGVhc3kuICAKCkknbSB1c2luZyB0aGUgZ2V0X3JlZ3Jlc3Npb25fcG9pbnRzIGZ1bmN0aW9uIHdoaWNoIGFkZHMgdGhlIG1vZGVsZWQgInktaGF0IiB2YWx1ZSB0byB0aGUgZGF0YWZyYW1lIG9mIGFsbCBvZiB0aGUgb3RoZXIgdmFsdWVzIFtnZXRfcmVncmVzc2lvbl9wb2ludHMoKV0oaHR0cHM6Ly93d3cucmRvY3VtZW50YXRpb24ub3JnL3BhY2thZ2VzL3N0YXRzL3ZlcnNpb25zLzMuNS4xL3RvcGljcy9maXR0ZWQpIGZ1bmN0aW9uLgoKVGhlIGZpdHRlZCB2ZXJzaW9uIGlzIHRoZSBkZXBlbmRlbnQgdmFyaWFibGUgdy8gYSAiX2hhdCIiIGF0IHRoZSBlbmQKCgpgYGB7cn0KCmZpdHRlZC5TX3ZfYWxsID0gZ2V0X3JlZ3Jlc3Npb25fcG9pbnRzKG1vZGVsID0gbGluZWFyX21vZGVsLlNfdl9hbGwpCgpwcmludChmaXR0ZWQuU192X2FsbCkKCmBgYAoKCkFuZCBmaW5hbGx5IHdlIGNhbiBwbG90IG91ciBhY3R1YWwgdnMgbW9kZWxlZCB2YWx1ZXMuICAoSSdtIGFkZGluZyBhIHRyZW5kIGxpbmUpCgoKYGBge3J9CgoKIyBNYWtpbmcgYSBzaW1wbGUgWC1ZIHNjYXR0ZXJwbG90IGFuZCBhZGRpbmcgYSByZWdyZXNzaW9uIHRvIGl0CgpnZ3Bsb3QoZGF0YSA9IGZpdHRlZC5TX3ZfYWxsKSArICAgICAgICAgICAjIGludm9rZSBncmFwaGljcyBlbnZpcm9ubWVudCB1c2luZyBhIGdpdmVuIGRhdGFmcmFtZQogIAogIHRoZW1lX2J3KCApICsgICAgICAgICAgICAgICAgICAgICAgICAgICAjIGNoYW5naW5nIHRoZSBwbG90dGluZyB0aGVtZQogIAogIGFlcyh4ICAgICAgPSBDb21wcmVzc2l2ZV9TdHJlbmd0aF8yOGR5LCAgICAjIHgtdmFsdWUKICAgICAgeSAgICAgID0gQ29tcHJlc3NpdmVfU3RyZW5ndGhfMjhkeV9oYXQpICsgICMgeS12YWx1ZQoKICBnZ3RpdGxlKCJZZWggU3VwZXJwbGFzdGljaXplciBUZXN0cyIsCiAgICAgICAgICBzdWJ0aXRsZSA9ICIyOC1keSBDb21wcmVzc2l2ZSBTdHJlbmd0aCAoTVBhKSIpICsgICAgIyBFRElURUQ6IEN1c3RvbSBUaXRsZSBub3cgd2l0aCBhIHN1YnRpdGxlCiAgCiAgeWxhYigiTW9kZWxsZWQiKSAgICAgKyAjIHktbGFiZWwKICB4bGFiKCJPYnNlcnZlZCIpICAgICArICMgeC1sYWJlbAoKICBnZW9tX3BvaW50KGNvbG91cj0iZGFya3JlZCIpICsgICMgcGxvdCBwb2ludHMKICAKICBnZW9tX3Ntb290aChtZXRob2QgID0gImxtIiwgICAgICAjIHVzZSBhIHNpbXBsZSBsaW5hciBtb2RlbAogICAgICAgICAgICAgIGZvcm11bGEgPSB5IH4geCwgICAgICMgbG0tc3R5bGUgZm9ybXVsYQogICAgICAgICAgICAgIHNlICAgICAgPSBUUlVFLCAgICAgICMgZGlzcGxheSBDb25maWRlbmNlIEludGVydmFscwogICAgICAgICAgICAgIGxldmVsICAgPSAwLjk1LCAgICAgICMgQ29uZmlkZW5lIExldmVsIHRvIE1hcCBPdXQKICAgICAgICAgICAgICBjb2xvdXIgID0gInJlZCIsICAgICAjIHJlZ3Jlc3Npb24gbGluZSBjb2xvcgogICAgICAgICAgICAgIGZpbGwgICAgPSAibWFnZW50YSIsICMgZmlsbCBmb3IgY29uZmlkZW5jZSBsaW1pdHMKICAgICAgICAgICAgICBzaXplICAgID0gMC41KSAgKyAgICAjIGxpbmUgdGhpY2tuZXNzCiAgCiAgZ2VvbV9hYmxpbmUoc2xvcGUgICAgID0gMSwgICAgICAgIyBORVc6IGFkZCBhIHZlcnkgc2ltcGxlIGxpbmUKICAgICAgICAgICAgICBpbnRlcmNlcHQgPSAwLCAgICAgICAjICAoZm9yIGEgMToxIHJlZmVyZW5jZSkKICAgICAgICAgICAgICBjb2xvciAgICAgPSAiZ3JleSIsCiAgICAgICAgICAgICAgbGluZXR5cGUgID0gImRhc2hlZCIpICsKCiAgY29vcmRfZml4ZWQocmF0aW8gPSAxKSAgICAgICAgICAgIyBORVc6IG1ha2UgdGhlIGFzcGVjdCByYXRpbyAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjICAgKEkgbGlrZSBteSBwbG90cyBzcXVhcmUpCmBgYAoKQW5kIGhlcmUgd2UgaGF2ZSBhIG5pY2UgcGxvdCBzaG93aW5nIG91ciB0cnVlIHZzIHByZWRpY3RlZCB2YWx1ZXMuCgojIDguIFJlZ3Jlc3Npb24gUXVhbGl0eSBNZXRyaWNzCgpBbmQgdG8gY2xvc2UgdGhpbmdzIG9mZiwgd2UgY2FuIGRvIHNvbWUgZ2VuZXJhbCBlcnJvciBtZXRyaWNzIHRoYXQgbWF5IGJlIHVzZWZ1bC4uCgpGaXJzdCwgdGhlIE1lYW4gU3F1YXJlZCBFcnJvciAoTVNFKSBvciBCaWFzLi4uIChpZiB3ZSBhcmUgdG9vIGhpZ2ggb3IgdG9vIGxvdykKCiRCSUFTID0gTVNFID0gXGZyYWN7MX17Tn0gIFxzdW1fe2k9MX1ee259IFtcd2lkZWhhdHt5fShcb3ZlcnJpZ2h0YXJyb3d7eF9pfSkteV9pXSAgPSAgICBcb3ZlcmxpbmV7W1x3aWRlaGF0e3l9KFxvdmVycmlnaHRhcnJvd3t4X2l9KS15X2ldfSQKCmBgYHtyfQogICMgQ2FsY3VsYXRlIEJpYXMgKE1TRSkKCiAgYmlhcyA9IG1lYW4oZml0dGVkLlNfdl9hbGwkQ29tcHJlc3NpdmVfU3RyZW5ndGhfMjhkeV9oYXQgLSAKICAgICAgICAgICAgICAgICBmaXR0ZWQuU192X2FsbCRDb21wcmVzc2l2ZV9TdHJlbmd0aF8yOGR5KQogIAogIHByaW50KHN0cl9jKCIgTWVhbiBTcXVhcmVkIEVycm9yIChNU0UpIG9yIEJpYXM6ICIsIGJpYXMpKQpgYGAKRm9yIGEgbGluZWFyIG9yIG11bHRpdmFyaWF0ZSByZWdyZXNzaW9uIHRoZSBhdmVyYWdlIG9mIG91ciByZXNpZHVhbHMgKHRoZSBkaWZmZXJlbmNlIGJldHdlZW4gZWFjaCBvYnNlcnZhdGlvbiBhbmQgcHJlZGljdGlvbikgKnNob3VsZCogYmUgemVyby4KClRoZSByb290IG1lYW4gc3F1YXJlZCBlcnJvciAoUk1TRSkgaXMgc2hvd24gaGVyZS4gIEl0IHNob3VsZG4ndCBiZSB6ZXJvIHNpbmNlIHRoZSByZXNpZHVhbHMgYXJlIHNxdWFyZWQgYmVmb3JlIHN1bW1pbmcgdGhlbSB1cC4gIFdlIHRlY2huaWNhbGx5IHNob3VsZCB1c2UgdGhlIHN0YW5kYXJkIGVycm9yIG9mIHRoZSBlc3RpbWF0ZSwgYnV0IFJNU0UgcmVtYWlucyBhIGNvbW1vbiBlcnJvciBtZXRyaWMuICAgV2UgY2FuIGFsd2F5cyBkbyBib3RoLiAgVGhlIHN0YW5kYXJkIGVycm9yIG9mIHRoZSBlc3RpbWF0ZSB0YWtlcyBpbnRvIGFjY291bnQgdGhlIGRlZ3JlZXMgb2YgZnJlZWRvbSB3aGljaCB3aGljaCBub3cgaW5jbHVkZXMgYWxsIG9mIHRoZSBpbmRlcGVuZGVudCB2YXJpYWJsZXMgKHApLiAgV2UgY2FuIGdldCB0aGUgc3RhbmRhcmQgZXJyb3Igb2YgdGhlIGVzdGltYXRlIGZyb20gb3VyIAoKJFJNU0UgPSBcc3FydHsgXGZyYWN7MX17Tn0gIFxzdW1fe2k9MX1ee259IFtcd2lkZWhhdHt5fShcb3ZlcnJpZ2h0YXJyb3d7eF9pfSkteV9pXV4yIH0gPSBcc3FydHtcb3ZlcmxpbmV7W1x3aWRlaGF0e3l9KFxvdmVycmlnaHRhcnJvd3t4X2l9KS15X2ldXjJ9ICAgICB9JAoKJHNfe2V9JCBvciAkc197eS94fSA9IFxzcXJ0eyBcZnJhY3sxfXtOLXAtMX0gIFxzdW1fe2k9MX1ee259IFtcd2lkZWhhdHt5fShcb3ZlcnJpZ2h0YXJyb3d7eF9pfSkteV9pXV4yIH0kCgoKYGBge3J9CiAgIyBDYWxjdWxhdGUgUk1TRQoKICBybXNlID0gc3FydChtZWFuKCAoZml0dGVkLlNfdl9hbGwkQ29tcHJlc3NpdmVfU3RyZW5ndGhfMjhkeV9oYXQgLQogICAgICAgICAgICAgICAgICAgICAgIGZpdHRlZC5TX3ZfYWxsJENvbXByZXNzaXZlX1N0cmVuZ3RoXzI4ZHkpXjIpICApCiAgCiAgcHJpbnQoc3RyX2MoIiAgICAgUm9vdCBNZWFuIFNxdWFyZWQgRXJyb3IgKFJNU0UpOiAiLCAgCiAgICAgICAgICAgICAgcm1zZSkpCiAgcHJpbnQoc3RyX2MoIlN0YW5kYXJkIEVycm9yIG9mIHRoZSBFc3RpbWF0ZSAoc2UpOiAiLCAKICAgICAgICAgICAgICBzdW1tYXJ5KGxpbmVhcl9tb2RlbC5TX3ZfYWxsKSRzaWdtYSkpICAjIHlvdSBoYXZlIHRvIGRpZyBmb3IgdGhpcyBvbmUhCmBgYAoKQW5kIGZpbmFsbHkgb3VyIGNvcnJlbGF0aW9uIGNvZWZmaWNpZW50ICh3aGljaCBpcyBiYXNpY2FsbHkgb3VyIGNvZWZmaWNpZW50IG9mIGRldGVybWluYXRpb24gYmVmb3JlIHRoZSAiUiIgaXMgInNxdWFyZWQiKQoKYGBge3J9CiAgIyBHZXQgVGhlIFVuYWRqdXN0ZWQgQ29ycmVsYXRpb24gQ29lZmZpY2llbnQKCiAgciA9IGNvcih4ID0gZml0dGVkLlNfdl9hbGwkQ29tcHJlc3NpdmVfU3RyZW5ndGhfMjhkeSwgICAgICMgdGhlIHgtdmFsdWUgCiAgICAgICAgICB5ID0gZml0dGVkLlNfdl9hbGwkQ29tcHJlc3NpdmVfU3RyZW5ndGhfMjhkeV9oYXQsICMgdGhlIHktdmFsdWUKICAgICAgICAgIG1ldGhvZCA9ICJwZWFyc29uIiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBtZXRob2Qgb2YgY29ycmVsYXRpb24KICAgICAgICAgICkKICAKICBwcmludChzdHJfYygiICAgICAgICAgICAgICAgICAgICAgICAgY29ycmVsYXRpb24gY29lZmZpY2llbnQgKHIpOiAiLCByKSkKICBwcmludChzdHJfYygiICAgICAgICAgICAgICAgICAgY29lZmZpY2llbnQgb2YgZGV0ZXJtaW5hdGlvbiAocsKyKTogIiwgcl4yLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiICIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdW1tYXJ5KGxpbmVhcl9tb2RlbC5TX3ZfYWxsKSRyLnNxdWFyZWQpKQogIHByaW50KHN0cl9jKCJhZGp1c3RlZCBjb2VmZmljaWVudCBvZiBkZXRlcm1pbmF0aW9uIChBZGp1c3RlZCBywrIpOiAiLCAKICAgICAgICAgICAgICAgc3VtbWFyeShsaW5lYXJfbW9kZWwuU192X2FsbCkkYWRqLnIuc3F1YXJlZCkpCgoKYGBgCgoKQW5kIHdpdGggdGhhdCwgd2UncmUgZG9uZS4uLiBPbmNlIGFnYWluLCB0aGlzIGV4ZXJjaXNlIGRlbW9uc3RyYXRlcyBhIGxvdCBvZiB0cmlja3MganVzdCB0byBzaG93IGhvdyB5b3UgY2FuIHVzZSBSIGZvciB2YXJpb3VzIHN0YXRpc3RpY3MuICBZb3UgbWF5IG5vdCB1c2UgYWxsIG9mIHRoZW0gaW4geW91ciBlbmNvdXRlcnMgd2l0aCBSIGZvciBsaW5lYXIgb3IgbXVsdGl2YXJpYXRlIHJlZ3Jlc3Npb24gb3IgZXZlbiBhdCBhbGwsIGJ1dCB5b3UgbWF5IGJlIGFibGUgdG8gY2FubmliYWxpemUgc29tZSBvZiB0aGUgdHJpY2tzIGhlcmUgZm9yIG90aGVyIGFwcGxpY2F0aW9ucy4KCgo=